From 37428c729f359a05837e0583824064141aeb2eba Mon Sep 17 00:00:00 2001 From: Stoyan Cheresharov Date: Thu, 30 May 2013 23:26:27 +0300 Subject: [PATCH] Addign Doctrine --- composer.json | 5 +- composer.phar | Bin 570295 -> 805675 bytes config/application.config.php | 3 + config/autoload/doctrineorm.local.php.dist | 22 + config/autoload/local.php.appfog.dist | 10 + .../Hydrator/.gitignore | 2 + data/DoctrineMongoODMModule/Proxy/.gitignore | 2 + data/DoctrineORMModule/Proxy/.gitignore | 2 + fmi.sql | 84 + localVirtualHost.conf.distribution | 13 + module/Fmi/Module.php | 42 +- module/Fmi/config/module.config.php | 79 + .../src/Fmi/Controller/IndexController.php | 326 ++ module/Fmi/src/Fmi/Entity/Album.php | 100 + .../Fmi/Entity/Repository/UserRepository.php | 77 + module/Fmi/src/Fmi/Entity/User.php | 392 ++ module/Fmi/view/fmi/index/add.phtml | 12 + module/Fmi/view/fmi/index/change-rage.phtml | 0 .../Fmi/view/fmi/index/change-waterdrop.phtml | 0 module/Fmi/view/fmi/index/delete.phtml | 27 + module/Fmi/view/fmi/index/edit.phtml | 13 + module/Fmi/view/fmi/index/get-managers.phtml | 2 + module/Fmi/view/fmi/index/index.phtml | 40 + module/Fmi/view/fmi/index/manage-user.phtml | 15 + public/htaccess.appfog | 9 + public/htaccess.localdev | 6 + public/htaccess.zend.phpcloud | 7 + vendor/bin/classmap_generator.php | 7 + vendor/bin/classmap_generator.php.bat | 3 + vendor/bin/doctrine | 7 + vendor/bin/doctrine-module | 7 + vendor/bin/doctrine-module.bat | 3 + vendor/bin/doctrine.bat | 3 + vendor/bin/doctrine.php | 7 + vendor/bin/doctrine.php.bat | 3 + vendor/doctrine/annotations/.gitignore | 1 + vendor/doctrine/annotations/.travis.yml | 8 + vendor/doctrine/annotations/README.md | 9 + vendor/doctrine/annotations/composer.json | 30 + .../Common/Annotations/Annotation.php | 79 + .../Annotations/Annotation/Attribute.php | 47 + .../Annotations/Annotation/Attributes.php | 37 + .../Common/Annotations/Annotation/Enum.php | 85 + .../Annotation/IgnoreAnnotation.php | 54 + .../Annotations/Annotation/Required.php | 33 + .../Common/Annotations/Annotation/Target.php | 107 + .../Annotations/AnnotationException.php | 158 + .../Common/Annotations/AnnotationReader.php | 318 ++ .../Common/Annotations/AnnotationRegistry.php | 139 + .../Common/Annotations/CachedReader.php | 250 ++ .../Doctrine/Common/Annotations/DocLexer.php | 132 + .../Doctrine/Common/Annotations/DocParser.php | 1041 +++++ .../Common/Annotations/FileCacheReader.php | 269 ++ .../Common/Annotations/IndexedReader.php | 141 + .../Doctrine/Common/Annotations/PhpParser.php | 89 + .../Doctrine/Common/Annotations/Reader.php | 67 + .../Annotations/SimpleAnnotationReader.php | 157 + .../Common/Annotations/TokenParser.php | 175 + vendor/doctrine/annotations/phpunit.xml.dist | 31 + .../Common/Annotations/AbstractReaderTest.php | 571 +++ .../Annotations/AnnotationReaderTest.php | 13 + .../Common/Annotations/CachedReaderTest.php | 56 + .../Tests/Common/Annotations/DocLexerTest.php | 137 + .../Common/Annotations/DocParserTest.php | 1264 ++++++ .../Tests/Common/Annotations/DummyClass.php | 48 + .../Annotations/FileCacheReaderTest.php | 40 + .../Annotation/AnnotWithDefaultValue.php | 10 + .../Annotations/Fixtures/Annotation/Route.php | 11 + .../Fixtures/Annotation/Secure.php | 18 + .../Fixtures/Annotation/Template.php | 14 + .../Fixtures/Annotation/Version.php | 11 + .../Annotations/Fixtures/AnnotationEnum.php | 21 + .../Fixtures/AnnotationEnumInvalid.php | 17 + .../Fixtures/AnnotationEnumLiteral.php | 34 + .../Fixtures/AnnotationEnumLiteralInvalid.php | 31 + .../Fixtures/AnnotationTargetAll.php | 14 + .../Fixtures/AnnotationTargetAnnotation.php | 14 + .../Fixtures/AnnotationTargetClass.php | 15 + .../Fixtures/AnnotationTargetMethod.php | 15 + .../AnnotationTargetPropertyMethod.php | 14 + .../Fixtures/AnnotationWithAttributes.php | 119 + .../Fixtures/AnnotationWithConstants.php | 20 + .../AnnotationWithRequiredAttributes.php | 50 + ...ithRequiredAttributesWithoutContructor.php | 24 + .../AnnotationWithTargetSyntaxError.php | 11 + .../Fixtures/AnnotationWithVarType.php | 62 + .../Tests/Common/Annotations/Fixtures/Api.php | 10 + .../Annotations/Fixtures/ClassDDC1660.php | 30 + .../Fixtures/ClassWithAnnotationEnum.php | 29 + ...assWithAnnotationWithTargetSyntaxError.php | 21 + .../ClassWithAnnotationWithVarType.php | 31 + .../Annotations/Fixtures/ClassWithClosure.php | 52 + .../Fixtures/ClassWithConstants.php | 10 + .../ClassWithFullyQualifiedUseStatements.php | 11 + ...lassWithInvalidAnnotationTargetAtClass.php | 17 + ...assWithInvalidAnnotationTargetAtMethod.php | 20 + ...sWithInvalidAnnotationTargetAtProperty.php | 24 + .../Annotations/Fixtures/ClassWithRequire.php | 15 + .../ClassWithValidAnnotationTarget.php | 41 + .../Annotations/Fixtures/Controller.php | 300 ++ ...erentNamespacesPerFileWithClassAsFirst.php | 15 + ...ferentNamespacesPerFileWithClassAsLast.php | 15 + ...EqualNamespacesPerFileWithClassAsFirst.php | 13 + .../EqualNamespacesPerFileWithClassAsLast.php | 12 + ...lobalNamespacesPerFileWithClassAsFirst.php | 12 + ...GlobalNamespacesPerFileWithClassAsLast.php | 12 + .../Fixtures/IntefaceWithConstants.php | 10 + .../InvalidAnnotationUsageButIgnoredClass.php | 14 + .../Fixtures/InvalidAnnotationUsageClass.php | 10 + .../Fixtures/MultipleClassesInFile.php | 9 + .../MultipleImportsInUseStatement.php | 10 + .../NamespaceAndClassCommentedOut.php | 20 + .../NamespaceWithClosureDeclaration.php | 12 + .../Fixtures/NamespacedSingleClassLOC1000.php | 1009 +++++ .../Annotations/Fixtures/NoAnnotation.php | 5 + .../Fixtures/NonNamespacedClass.php | 10 + .../Fixtures/SingleClassLOC1000.php | 1006 +++++ .../Annotations/Fixtures/TestInterface.php | 13 + .../Common/Annotations/PerformanceTest.php | 194 + .../Common/Annotations/PhpParserTest.php | 207 + .../SimpleAnnotationReaderTest.php | 97 + .../Common/Annotations/Ticket/DCOM55Test.php | 65 + .../Annotations/Ticket/DCOM58Entity.php | 8 + .../Common/Annotations/Ticket/DCOM58Test.php | 112 + .../Common/Annotations/TopLevelAnnotation.php | 8 + .../tests/Doctrine/Tests/DoctrineTestCase.php | 10 + .../tests/Doctrine/Tests/TestInit.php | 26 + vendor/doctrine/cache/.gitignore | 1 + vendor/doctrine/cache/.travis.yml | 8 + vendor/doctrine/cache/LICENSE | 19 + vendor/doctrine/cache/README.md | 3 + vendor/doctrine/cache/composer.json | 21 + .../lib/Doctrine/Common/Cache/ApcCache.php | 93 + .../lib/Doctrine/Common/Cache/ArrayCache.php | 96 + .../cache/lib/Doctrine/Common/Cache/Cache.php | 102 + .../Doctrine/Common/Cache/CacheProvider.php | 231 ++ .../Doctrine/Common/Cache/CouchbaseCache.php | 123 + .../lib/Doctrine/Common/Cache/FileCache.php | 132 + .../Doctrine/Common/Cache/FilesystemCache.php | 114 + .../Doctrine/Common/Cache/MemcacheCache.php | 121 + .../Doctrine/Common/Cache/MemcachedCache.php | 124 + .../Doctrine/Common/Cache/PhpFileCache.php | 108 + .../lib/Doctrine/Common/Cache/RedisCache.php | 119 + .../Doctrine/Common/Cache/WinCacheCache.php | 93 + .../lib/Doctrine/Common/Cache/XcacheCache.php | 110 + .../Doctrine/Common/Cache/ZendDataCache.php | 84 + vendor/doctrine/cache/phpunit.xml.dist | 31 + .../Tests/Common/Cache/ApcCacheTest.php | 20 + .../Tests/Common/Cache/ArrayCacheTest.php | 21 + .../Doctrine/Tests/Common/Cache/CacheTest.php | 103 + .../Tests/Common/Cache/CouchbaseCacheTest.php | 47 + .../Common/Cache/FilesystemCacheTest.php | 97 + .../Tests/Common/Cache/MemcacheCacheTest.php | 45 + .../Tests/Common/Cache/MemcachedCacheTest.php | 48 + .../Tests/Common/Cache/PhpFileCacheTest.php | 149 + .../Tests/Common/Cache/RedisCacheTest.php | 30 + .../Tests/Common/Cache/WinCacheCacheTest.php | 20 + .../Tests/Common/Cache/XcacheCacheTest.php | 20 + .../Tests/Common/Cache/ZendDataCacheTest.php | 28 + .../tests/Doctrine/Tests/DoctrineTestCase.php | 10 + .../cache/tests/Doctrine/Tests/TestInit.php | 23 + vendor/doctrine/collections/.gitignore | 1 + vendor/doctrine/collections/.travis.yml | 9 + vendor/doctrine/collections/README.md | 3 + vendor/doctrine/collections/composer.json | 26 + .../Common/Collections/ArrayCollection.php | 385 ++ .../Common/Collections/Collection.php | 261 ++ .../Doctrine/Common/Collections/Criteria.php | 245 ++ .../Expr/ClosureExpressionVisitor.php | 224 ++ .../Common/Collections/Expr/Comparison.php | 103 + .../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 | 162 + .../Common/Collections/Selectable.php | 48 + vendor/doctrine/collections/phpunit.xml.dist | 31 + .../ClosureExpressionVisitorTest.php | 243 ++ .../Common/Collections/CollectionTest.php | 264 ++ .../Tests/Common/Collections/CriteriaTest.php | 82 + .../Collections/ExpressionBuilderTest.php | 122 + .../tests/Doctrine/Tests/DoctrineTestCase.php | 10 + .../tests/Doctrine/Tests/TestInit.php | 21 + vendor/doctrine/common/.gitignore | 3 + vendor/doctrine/common/.travis.yml | 9 + vendor/doctrine/common/LICENSE | 523 +-- vendor/doctrine/common/bin/travis-setup.php | 141 + vendor/doctrine/common/composer.json | 18 +- .../lib/Doctrine/Common/ClassLoader.php | 14 +- .../lib/Doctrine/Common/CommonException.php | 4 +- .../common/lib/Doctrine/Common/Comparable.php | 4 +- .../common/lib/Doctrine/Common/EventArgs.php | 4 +- .../lib/Doctrine/Common/EventManager.php | 17 +- .../lib/Doctrine/Common/EventSubscriber.php | 4 +- .../common/lib/Doctrine/Common/Lexer.php | 245 +- .../Doctrine/Common/NotifyPropertyChanged.php | 5 +- .../Persistence/AbstractManagerRegistry.php | 59 +- .../Common/Persistence/ConnectionRegistry.php | 12 +- .../Persistence/Event/LifecycleEventArgs.php | 27 +- .../Event/LoadClassMetadataEventArgs.php | 4 +- .../Persistence/Event/ManagerEventArgs.php | 4 +- .../Persistence/Event/OnClearEventArgs.php | 10 +- .../Persistence/Event/PreUpdateEventArgs.php | 6 +- .../Common/Persistence/ManagerRegistry.php | 20 +- .../Mapping/AbstractClassMetadataFactory.php | 108 +- .../Persistence/Mapping/ClassMetadata.php | 36 +- .../Mapping/ClassMetadataFactory.php | 12 +- .../Mapping/Driver/AnnotationDriver.php | 6 +- .../Mapping/Driver/DefaultFileLocator.php | 13 +- .../Persistence/Mapping/Driver/FileDriver.php | 44 +- .../Mapping/Driver/FileLocator.php | 16 +- .../Mapping/Driver/MappingDriver.php | 10 +- .../Mapping/Driver/MappingDriverChain.php | 59 +- .../Persistence/Mapping/Driver/PHPDriver.php | 4 +- .../Mapping/Driver/StaticPHPDriver.php | 12 +- .../Mapping/Driver/SymfonyFileLocator.php | 18 +- .../Persistence/Mapping/MappingException.php | 41 +- .../Persistence/Mapping/ReflectionService.php | 21 +- .../Mapping/RuntimeReflectionService.php | 60 +- .../Mapping/StaticReflectionService.php | 2 +- .../Common/Persistence/ObjectManager.php | 37 +- .../Common/Persistence/ObjectManagerAware.php | 4 +- .../Persistence/ObjectManagerDecorator.php | 140 + .../Common/Persistence/ObjectRepository.php | 16 +- .../Common/Persistence/PersistentObject.php | 27 +- .../lib/Doctrine/Common/Persistence/Proxy.php | 4 +- .../Common/PropertyChangedListener.php | 2 +- .../Common/Proxy/AbstractProxyFactory.php | 184 + .../lib/Doctrine/Common/Proxy/Autoloader.php | 93 + .../Exception/InvalidArgumentException.php | 90 + .../Common/Proxy/Exception/ProxyException.php | 31 + .../Exception/UnexpectedValueException.php | 61 + .../lib/Doctrine/Common/Proxy/Proxy.php | 83 + .../Doctrine/Common/Proxy/ProxyDefinition.php | 70 + .../Doctrine/Common/Proxy/ProxyGenerator.php | 896 +++++ .../Reflection/ClassFinderInterface.php | 38 + .../Common/Reflection/Psr0FindFile.php | 83 + .../ReflectionProviderInterface.php | 45 + .../RuntimePublicReflectionProperty.php | 76 + .../Reflection/StaticReflectionClass.php | 112 + .../Reflection/StaticReflectionMethod.php | 103 + .../Reflection/StaticReflectionParser.php | 295 ++ .../Reflection/StaticReflectionProperty.php | 77 + .../lib/Doctrine/Common/Util/ClassUtils.php | 6 +- .../common/lib/Doctrine/Common/Util/Debug.php | 22 +- .../lib/Doctrine/Common/Util/Inflector.php | 54 +- .../common/lib/Doctrine/Common/Version.php | 4 +- .../Doctrine/Tests/Common/ClassLoaderTest.php | 19 + .../Tests/Common/ClassLoaderTest/ClassE.php | 5 + .../Persistence/Mapping/ChainDriverTest.php | 62 +- .../Mapping/ClassMetadataFactoryTest.php | 13 +- .../Mapping/DefaultFileLocatorTest.php | 2 +- .../Mapping/RuntimeReflectionServiceTest.php | 14 + .../ObjectManagerDecoratorTest.php | 60 + .../Persistence/PersistentObjectTest.php | 6 +- .../Common/Proxy/AbstractProxyFactoryTest.php | 118 + .../Tests/Common/Proxy/AutoloaderTest.php | 72 + .../Common/Proxy/CallableTypeHintClass.php | 16 + .../Common/Proxy/InvalidTypeHintClass.php | 16 + .../Tests/Common/Proxy/LazyLoadableObject.php | 115 + .../Proxy/LazyLoadableObjectClassMetadata.php | 195 + .../Tests/Common/Proxy/MagicCloneClass.php | 37 + .../Tests/Common/Proxy/MagicGetClass.php | 38 + .../Tests/Common/Proxy/MagicIssetClass.php | 38 + .../Tests/Common/Proxy/MagicSetClass.php | 43 + .../Tests/Common/Proxy/MagicSleepClass.php | 37 + .../Tests/Common/Proxy/MagicWakeupClass.php | 32 + .../Common/Proxy/ProxyClassGeneratorTest.php | 185 + .../Tests/Common/Proxy/ProxyLogicTest.php | 696 ++++ .../Common/Proxy/ProxyMagicMethodsTest.php | 285 ++ .../Tests/Common/Proxy/SleepClass.php | 19 + .../Reflection/DeeperNamespaceParent.php | 7 + .../Common/Reflection/Dummies/NoParent.php | 8 + .../Reflection/FullyClassifiedParent.php | 7 + .../Tests/Common/Reflection/NoParent.php | 8 + .../RuntimePublicReflectionPropertyTest.php | 192 + .../Common/Reflection/SameNamespaceParent.php | 7 + .../Reflection/StaticReflectionParserTest.php | 63 + .../Tests/Common/Reflection/UseParent.php | 9 + .../Doctrine/Tests/Common/Util/DebugTest.php | 13 + .../common/tests/Doctrine/Tests/TestInit.php | 11 +- .../common/tests/NativePhpunitTask.php | 4 +- vendor/doctrine/dbal/LICENSE | 19 + vendor/doctrine/dbal/README.md | 14 + vendor/doctrine/dbal/UPGRADE | 148 + vendor/doctrine/dbal/bin/doctrine-dbal | 4 + vendor/doctrine/dbal/bin/doctrine-dbal.php | 43 + vendor/doctrine/dbal/bin/doctrine.php | 42 + vendor/doctrine/dbal/composer.json | 36 + .../Doctrine/DBAL/Cache/ArrayStatement.php | 103 + .../Doctrine/DBAL/Cache/CacheException.php | 37 + .../Doctrine/DBAL/Cache/QueryCacheProfile.php | 131 + .../DBAL/Cache/ResultCacheStatement.php | 239 ++ .../dbal/lib/Doctrine/DBAL/Configuration.php | 113 + .../dbal/lib/Doctrine/DBAL/Connection.php | 1345 +++++++ .../lib/Doctrine/DBAL/ConnectionException.php | 54 + .../Connections/MasterSlaveConnection.php | 353 ++ .../dbal/lib/Doctrine/DBAL/DBALException.php | 128 + .../dbal/lib/Doctrine/DBAL/Driver.php | 72 + .../lib/Doctrine/DBAL/Driver/Connection.php | 42 + .../Driver/DrizzlePDOMySql/Connection.php | 41 + .../DBAL/Driver/DrizzlePDOMySql/Driver.php | 99 + .../DBAL/Driver/IBMDB2/DB2Connection.php | 115 + .../Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php | 111 + .../DBAL/Driver/IBMDB2/DB2Exception.php | 27 + .../DBAL/Driver/IBMDB2/DB2Statement.php | 214 ++ .../Doctrine/DBAL/Driver/Mysqli/Driver.php | 69 + .../DBAL/Driver/Mysqli/MysqliConnection.php | 146 + .../DBAL/Driver/Mysqli/MysqliException.php | 26 + .../DBAL/Driver/Mysqli/MysqliStatement.php | 342 ++ .../lib/Doctrine/DBAL/Driver/OCI8/Driver.php | 99 + .../DBAL/Driver/OCI8/OCI8Connection.php | 200 + .../DBAL/Driver/OCI8/OCI8Exception.php | 30 + .../DBAL/Driver/OCI8/OCI8Statement.php | 270 ++ .../Doctrine/DBAL/Driver/PDOConnection.php | 40 + .../Doctrine/DBAL/Driver/PDOIbm/Driver.php | 126 + .../Doctrine/DBAL/Driver/PDOMySql/Driver.php | 102 + .../Doctrine/DBAL/Driver/PDOOracle/Driver.php | 98 + .../Doctrine/DBAL/Driver/PDOPgSql/Driver.php | 73 + .../Doctrine/DBAL/Driver/PDOSqlite/Driver.php | 116 + .../DBAL/Driver/PDOSqlsrv/Connection.php | 45 + .../Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php | 87 + .../lib/Doctrine/DBAL/Driver/PDOStatement.php | 50 + .../Doctrine/DBAL/Driver/ResultStatement.php | 91 + .../Doctrine/DBAL/Driver/SQLSrv/Driver.php | 72 + .../DBAL/Driver/SQLSrv/LastInsertId.php | 42 + .../DBAL/Driver/SQLSrv/SQLSrvConnection.php | 161 + .../DBAL/Driver/SQLSrv/SQLSrvException.php | 43 + .../DBAL/Driver/SQLSrv/SQLSrvStatement.php | 251 ++ .../lib/Doctrine/DBAL/Driver/Statement.php | 123 + .../dbal/lib/Doctrine/DBAL/DriverManager.php | 176 + .../DBAL/Event/ConnectionEventArgs.php | 79 + .../DBAL/Event/Listeners/MysqlSessionInit.php | 74 + .../Event/Listeners/OracleSessionInit.php | 80 + .../DBAL/Event/Listeners/SQLSessionInit.php | 63 + .../SchemaAlterTableAddColumnEventArgs.php | 114 + .../SchemaAlterTableChangeColumnEventArgs.php | 114 + .../DBAL/Event/SchemaAlterTableEventArgs.php | 99 + .../SchemaAlterTableRemoveColumnEventArgs.php | 114 + .../SchemaAlterTableRenameColumnEventArgs.php | 129 + .../Event/SchemaColumnDefinitionEventArgs.php | 137 + .../SchemaCreateTableColumnEventArgs.php | 114 + .../DBAL/Event/SchemaCreateTableEventArgs.php | 128 + .../DBAL/Event/SchemaDropTableEventArgs.php | 98 + .../Doctrine/DBAL/Event/SchemaEventArgs.php | 56 + .../Event/SchemaIndexDefinitionEventArgs.php | 122 + .../dbal/lib/Doctrine/DBAL/Events.php | 48 + .../lib/Doctrine/DBAL/Id/TableGenerator.php | 160 + .../DBAL/Id/TableGeneratorSchemaVisitor.php | 90 + .../dbal/lib/Doctrine/DBAL/LockMode.php | 42 + .../lib/Doctrine/DBAL/Logging/DebugStack.php | 69 + .../Doctrine/DBAL/Logging/EchoSQLLogger.php | 61 + .../lib/Doctrine/DBAL/Logging/LoggerChain.php | 64 + .../lib/Doctrine/DBAL/Logging/SQLLogger.php | 54 + .../DBAL/Platforms/AbstractPlatform.php | 2844 ++++++++++++++ .../Doctrine/DBAL/Platforms/DB2Platform.php | 545 +++ .../DBAL/Platforms/DrizzlePlatform.php | 495 +++ .../DBAL/Platforms/Keywords/DB2Keywords.php | 438 +++ .../Platforms/Keywords/DrizzleKeywords.php | 340 ++ .../DBAL/Platforms/Keywords/KeywordList.php | 63 + .../DBAL/Platforms/Keywords/MsSQLKeywords.php | 43 + .../DBAL/Platforms/Keywords/MySQLKeywords.php | 269 ++ .../Platforms/Keywords/OracleKeywords.php | 157 + .../Platforms/Keywords/PostgreSQLKeywords.php | 131 + .../Keywords/ReservedKeywordsValidator.php | 116 + .../Keywords/SQLServer2005Keywords.php | 56 + .../Keywords/SQLServer2008Keywords.php | 51 + .../Keywords/SQLServer2012Keywords.php | 55 + .../Platforms/Keywords/SQLServerKeywords.php | 231 ++ .../Platforms/Keywords/SQLiteKeywords.php | 164 + .../Doctrine/DBAL/Platforms/MySqlPlatform.php | 821 ++++ .../DBAL/Platforms/OraclePlatform.php | 836 ++++ .../DBAL/Platforms/PostgreSqlPlatform.php | 822 ++++ .../DBAL/Platforms/SQLAzurePlatform.php | 51 + .../DBAL/Platforms/SQLServer2005Platform.php | 64 + .../DBAL/Platforms/SQLServer2008Platform.php | 110 + .../DBAL/Platforms/SQLServer2012Platform.php | 98 + .../DBAL/Platforms/SQLServerPlatform.php | 1150 ++++++ .../DBAL/Platforms/SqlitePlatform.php | 987 +++++ .../Doctrine/DBAL/Portability/Connection.php | 119 + .../Doctrine/DBAL/Portability/Statement.php | 195 + .../Query/Expression/CompositeExpression.php | 130 + .../Query/Expression/ExpressionBuilder.php | 303 ++ .../lib/Doctrine/DBAL/Query/QueryBuilder.php | 1099 ++++++ .../Doctrine/DBAL/Query/QueryException.php | 38 + .../dbal/lib/Doctrine/DBAL/README.markdown | 0 .../dbal/lib/Doctrine/DBAL/SQLParserUtils.php | 234 ++ .../Doctrine/DBAL/SQLParserUtilsException.php | 43 + .../Doctrine/DBAL/Schema/AbstractAsset.php | 214 ++ .../DBAL/Schema/AbstractSchemaManager.php | 896 +++++ .../dbal/lib/Doctrine/DBAL/Schema/Column.php | 423 +++ .../lib/Doctrine/DBAL/Schema/ColumnDiff.php | 64 + .../lib/Doctrine/DBAL/Schema/Comparator.php | 436 +++ .../lib/Doctrine/DBAL/Schema/Constraint.php | 42 + .../Doctrine/DBAL/Schema/DB2SchemaManager.php | 214 ++ .../DBAL/Schema/DrizzleSchemaManager.php | 96 + .../DBAL/Schema/ForeignKeyConstraint.php | 212 ++ .../dbal/lib/Doctrine/DBAL/Schema/Index.php | 252 ++ .../DBAL/Schema/MySqlSchemaManager.php | 212 ++ .../DBAL/Schema/OracleSchemaManager.php | 286 ++ .../DBAL/Schema/PostgreSqlSchemaManager.php | 369 ++ .../DBAL/Schema/SQLServerSchemaManager.php | 256 ++ .../dbal/lib/Doctrine/DBAL/Schema/Schema.php | 373 ++ .../lib/Doctrine/DBAL/Schema/SchemaConfig.php | 119 + .../lib/Doctrine/DBAL/Schema/SchemaDiff.php | 181 + .../Doctrine/DBAL/Schema/SchemaException.php | 126 + .../lib/Doctrine/DBAL/Schema/Sequence.php | 120 + .../DBAL/Schema/SqliteSchemaManager.php | 371 ++ .../AbstractSchemaSynchronizer.php | 58 + .../Synchronizer/SchemaSynchronizer.php | 96 + .../SingleDatabaseSynchronizer.php | 197 + .../dbal/lib/Doctrine/DBAL/Schema/Table.php | 680 ++++ .../lib/Doctrine/DBAL/Schema/TableDiff.php | 143 + .../dbal/lib/Doctrine/DBAL/Schema/View.php | 53 + .../DBAL/Schema/Visitor/AbstractVisitor.php | 80 + .../Visitor/CreateSchemaSqlCollector.php | 157 + .../Schema/Visitor/DropSchemaSqlCollector.php | 132 + .../Doctrine/DBAL/Schema/Visitor/Graphviz.php | 148 + .../Schema/Visitor/RemoveNamespacedAssets.php | 96 + .../DBAL/Schema/Visitor/SchemaDiffVisitor.php | 68 + .../Doctrine/DBAL/Schema/Visitor/Visitor.php | 75 + .../DBAL/Sharding/PoolingShardConnection.php | 201 + .../DBAL/Sharding/PoolingShardManager.php | 98 + .../SQLAzureFederationsSynchronizer.php | 297 ++ .../SQLAzure/SQLAzureShardManager.php | 238 ++ .../SQLAzure/Schema/MultiTenantVisitor.php | 161 + .../ShardChoser/MultiTenantShardChoser.php | 37 + .../DBAL/Sharding/ShardChoser/ShardChoser.php | 41 + .../Doctrine/DBAL/Sharding/ShardManager.php | 95 + .../DBAL/Sharding/ShardingException.php | 61 + .../dbal/lib/Doctrine/DBAL/Statement.php | 268 ++ .../Tools/Console/Command/ImportCommand.php | 124 + .../Console/Command/ReservedWordsCommand.php | 150 + .../Tools/Console/Command/RunSqlCommand.php | 87 + .../Tools/Console/Helper/ConnectionHelper.php | 74 + .../lib/Doctrine/DBAL/Types/ArrayType.php | 65 + .../lib/Doctrine/DBAL/Types/BigIntType.php | 56 + .../dbal/lib/Doctrine/DBAL/Types/BlobType.php | 71 + .../lib/Doctrine/DBAL/Types/BooleanType.php | 57 + .../DBAL/Types/ConversionException.php | 65 + .../lib/Doctrine/DBAL/Types/DateTimeType.php | 59 + .../Doctrine/DBAL/Types/DateTimeTzType.php | 79 + .../dbal/lib/Doctrine/DBAL/Types/DateType.php | 59 + .../lib/Doctrine/DBAL/Types/DecimalType.php | 45 + .../lib/Doctrine/DBAL/Types/FloatType.php | 54 + .../dbal/lib/Doctrine/DBAL/Types/GuidType.php | 47 + .../lib/Doctrine/DBAL/Types/IntegerType.php | 53 + .../lib/Doctrine/DBAL/Types/JsonArrayType.php | 66 + .../lib/Doctrine/DBAL/Types/ObjectType.php | 64 + .../Doctrine/DBAL/Types/SimpleArrayType.php | 69 + .../lib/Doctrine/DBAL/Types/SmallIntType.php | 52 + .../lib/Doctrine/DBAL/Types/StringType.php | 50 + .../dbal/lib/Doctrine/DBAL/Types/TextType.php | 56 + .../dbal/lib/Doctrine/DBAL/Types/TimeType.php | 68 + .../dbal/lib/Doctrine/DBAL/Types/Type.php | 306 ++ .../Doctrine/DBAL/Types/VarDateTimeType.php | 60 + .../dbal/lib/Doctrine/DBAL/Version.php | 55 + vendor/doctrine/doctrine-module/.gitignore | 0 vendor/doctrine/doctrine-module/.travis.yml | 22 + .../.travis/application.config.php | 23 + .../doctrine-module/.travis/composer.json | 6 + vendor/doctrine/doctrine-module/LICENSE | 14 + vendor/doctrine/doctrine-module/Module.php | 7 + vendor/doctrine/doctrine-module/README.md | 26 + .../doctrine-module/bin/doctrine-module | 4 + .../doctrine-module/bin/doctrine-module.bat | 9 + .../doctrine-module/bin/doctrine-module.php | 50 + vendor/doctrine/doctrine-module/composer.json | 68 + .../doctrine-module/config/module.config.php | 56 + .../doctrine-module/docs/authentication.md | 179 + .../doctrine/doctrine-module/docs/caching.md | 24 + vendor/doctrine/doctrine-module/docs/cli.md | 46 + .../doctrine-module/docs/form-element.md | 86 + .../doctrine/doctrine-module/docs/hydrator.md | 1459 +++++++ vendor/doctrine/doctrine-module/docs/index.md | 19 + .../doctrine-module/docs/paginator.md | 30 + .../doctrine-module/docs/validator.md | 162 + .../Adapter/ObjectRepository.php | 240 ++ .../Storage/ObjectRepository.php | 118 + .../Cache/DoctrineCacheStorage.php | 90 + .../DoctrineModule/Cache/ZendStorageCache.php | 123 + .../Form/Element/ObjectMultiCheckbox.php | 72 + .../Form/Element/ObjectRadio.php | 72 + .../Form/Element/ObjectSelect.php | 72 + .../src/DoctrineModule/Form/Element/Proxy.php | 361 ++ .../src/DoctrineModule/Module.php | 102 + .../DoctrineModule/Options/Authentication.php | 310 ++ .../src/DoctrineModule/Options/Cache.php | 137 + .../src/DoctrineModule/Options/Driver.php | 155 + .../DoctrineModule/Options/EventManager.php | 60 + .../Paginator/Adapter/Collection.php | 63 + .../ObjectManagerAwareInterface.php | 39 + .../Persistence/ProvidesObjectManager.php | 53 + .../Service/AbstractFactory.php | 100 + .../Service/Authentication/AdapterFactory.php | 53 + .../AuthenticationServiceFactory.php | 50 + .../Service/Authentication/StorageFactory.php | 53 + .../DoctrineModule/Service/CacheFactory.php | 85 + .../src/DoctrineModule/Service/CliFactory.php | 89 + .../DoctrineModule/Service/DriverFactory.php | 135 + .../Service/EventManagerFactory.php | 79 + .../Service/ZendStorageCacheFactory.php | 64 + .../Stdlib/Hydrator/DoctrineObject.php | 435 +++ .../Strategy/AbstractCollectionStrategy.php | 183 + .../Strategy/AllowRemoveByReference.php | 58 + .../Hydrator/Strategy/AllowRemoveByValue.php | 66 + .../Strategy/DisallowRemoveByReference.php | 53 + .../Strategy/DisallowRemoveByValue.php | 63 + .../Validator/NoObjectExists.php | 60 + .../DoctrineModule/Validator/ObjectExists.php | 188 + .../DoctrineModule/Validator/UniqueObject.php | 166 + .../src/DoctrineModule/Version.php | 33 + .../doctrine/doctrine-module/tests/.gitignore | 1 + .../doctrine-module/tests/Bootstrap.php | 39 + .../Adapter/ObjectRepositoryTest.php | 294 ++ .../Adapter/TestAsset/IdentityObject.php | 72 + .../PublicPropertiesIdentityObject.php | 40 + .../Storage/ObjectRepositoryTest.php | 70 + .../Cache/DoctrineCacheStorageTest.php | 729 ++++ .../Cache/ZendStorageCacheTest.php | 113 + .../Form/Element/ProxyTest.php | 372 ++ .../Form/Element/TestAsset/FormObject.php | 167 + .../Adapter/CollectionAdapterTest.php | 74 + .../Authentication/AdapterFactoryTest.php | 56 + .../AuthenticationServiceFactoryTest.php | 60 + .../Authentication/StorageFactoryTest.php | 55 + .../Service/EventManagerFactoryTest.php | 146 + .../TestAsset/DummyEventSubscriber.php | 45 + .../ServiceManagerTestCase.php | 72 + .../Stdlib/Hydrator/Asset/OneToManyEntity.php | 66 + .../Stdlib/Hydrator/Asset/OneToOneEntity.php | 49 + .../Stdlib/Hydrator/Asset/SimpleEntity.php | 47 + .../Asset/SimpleEntityWithDateTime.php | 38 + .../Stdlib/Hydrator/DoctrineObjectTest.php | 1190 ++++++ .../Validator/NoObjectExistsTest.php | 64 + .../Validator/ObjectExistsTest.php | 174 + .../Validator/UniqueObjectTest.php | 244 ++ .../tests/TestConfiguration.php.dist | 15 + .../doctrine-module/tests/phpunit.xml | 11 + vendor/doctrine/doctrine-mongo-odm-module | 1 + .../doctrine/doctrine-orm-module/.travis.yml | 14 + .../.travis/Version20120714005702.php | 44 + .../.travis/application.config.php | 25 + .../.travis/dummy-import.sql | 1 + .../.travis/migrations-config.xml | 11 + .../.travis/migrations-execute-config.xml | 11 + .../doctrine-orm-module/.travis/run-cli.sh | 17 + vendor/doctrine/doctrine-orm-module/LICENSE | 14 + .../doctrine/doctrine-orm-module/Module.php | 7 + vendor/doctrine/doctrine-orm-module/README.md | 122 + .../doctrine/doctrine-orm-module/UPGRADE.md | 19 + .../doctrine-orm-module/composer.json | 64 + .../config/controllers.config.php | 40 + .../config/module.config.php | 182 + .../config/services.config.php | 47 + .../doctrine-orm-module/coverage-checker.php | 60 + .../doctrine-orm-module/docs/EXTRAS_ORM.md | 116 + .../doctrine-orm-module/docs/configuration.md | 175 + .../docs/developer-tools.md | 110 + ...veloper-tools-multiple-entity-managers.png | Bin 0 -> 27793 bytes ...2-zend-developer-tools-doctrine-module.png | Bin 0 -> 29755 bytes .../doctrine/doctrine-orm-module/phpunit.xml | 25 + .../Collector/MappingCollector.php | 139 + .../Collector/SQLLoggerCollector.php | 123 + .../Form/Annotation/AnnotationBuilder.php | 61 + .../Annotation/ElementAnnotationsListener.php | 237 ++ .../Form/Element/DoctrineEntity.php | 27 + .../Form/Element/EntityMultiCheckbox.php | 26 + .../Form/Element/EntityRadio.php | 26 + .../Form/Element/EntitySelect.php | 26 + .../src/DoctrineORMModule/Module.php | 150 + .../Options/Configuration.php | 464 +++ .../Options/DBALConfiguration.php | 104 + .../Options/DBALConnection.php | 202 + .../Options/EntityManager.php | 65 + .../Options/EntityResolver.php | 73 + .../Options/SQLLoggerCollectorOptions.php | 101 + .../Paginator/Adapter/DoctrinePaginator.php | 87 + .../Service/ConfigurationFactory.php | 90 + .../Service/DBALConfigurationFactory.php | 116 + .../Service/DBALConnectionFactory.php | 77 + .../Service/EntityManagerFactory.php | 49 + .../Service/EntityResolverFactory.php | 59 + .../Service/SQLLoggerCollectorFactory.php | 116 + .../Stdlib/Hydrator/DoctrineEntity.php | 37 + .../src/DoctrineORMModule/Version.php | 33 + .../Yuml/MetadataGrapher.php | 248 ++ .../DoctrineORMModule/Yuml/YumlController.php | 72 + .../doctrine-orm-module/tests/.gitignore | 1 + .../doctrine-orm-module/tests/Bootstrap.php | 34 + .../Assets/Entity/Category.php | 44 + .../Assets/Entity/City.php | 61 + .../Assets/Entity/Country.php | 44 + .../Assets/Entity/Date.php | 48 + .../Assets/Entity/Product.php | 61 + .../Assets/Entity/ResolveTarget.php | 23 + .../Assets/Entity/TargetEntity.php | 18 + .../Assets/Entity/TargetInterface.php | 8 + .../Assets/Entity/Test.php | 79 + .../Assets/Fixture/TestFixture.php | 65 + .../Assets/GraphEntity/Address.php | 51 + .../Assets/GraphEntity/Session.php | 48 + .../Assets/GraphEntity/User.php | 70 + .../Assets/GraphEntity/UserGroup.php | 55 + .../tests/DoctrineORMModuleTest/CliTest.php | 143 + .../Collector/MappingCollectorTest.php | 138 + .../Collector/SQLLoggerCollectorTest.php | 101 + .../Form/ElementAnnotationsListenerTest.php | 59 + .../Framework/TestCase.php | 88 + .../Options/ConfigurationOptionsTest.php | 43 + .../Options/SQLLoggerCollectorOptionsTest.php | 54 + .../Paginator/AdapterTestIgnore.php | 108 + .../Service/ConfigurationFactoryTest.php | 119 + .../Service/EntityResolverFactoryTest.php | 34 + .../Service/SQLLoggerCollectorFactoryTest.php | 165 + .../Util/ServiceManagerFactory.php | 64 + .../Yuml/MetadataGrapherTest.php | 395 ++ .../Yuml/YumlControllerTest.php | 98 + .../tests/TestConfiguration.php.dist | 30 + .../tests/testing.config.php | 53 + .../toolbar/doctrine-orm-mappings.phtml | 73 + .../toolbar/doctrine-orm-queries.phtml | 85 + vendor/doctrine/inflector/README.md | 4 + vendor/doctrine/inflector/composer.json | 21 + .../Doctrine/Common/Inflector/Inflector.php | 385 ++ vendor/doctrine/inflector/phpunit.xml.dist | 31 + .../Tests/Common/Inflector/InflectorTest.php | 185 + .../tests/Doctrine/Tests/DoctrineTestCase.php | 10 + .../tests/Doctrine/Tests/TestInit.php | 27 + vendor/doctrine/lexer/README.md | 5 + vendor/doctrine/lexer/composer.json | 19 + .../Doctrine/Common/Lexer/AbstractLexer.php | 265 ++ vendor/doctrine/mongodb-odm/.gitignore | 14 + vendor/doctrine/mongodb-odm/.travis.yml | 15 + vendor/doctrine/mongodb-odm/LICENSE | 19 + vendor/doctrine/mongodb-odm/README.markdown | 13 + .../doctrine/mongodb-odm/build.properties.dev | 11 + vendor/doctrine/mongodb-odm/build.xml | 107 + vendor/doctrine/mongodb-odm/composer.json | 34 + .../mongodb-odm/doctrine-mongo-mapping.xsd | 248 ++ .../Doctrine/ODM/MongoDB/Configuration.php | 369 ++ .../lib/Doctrine/ODM/MongoDB/Cursor.php | 181 + .../Doctrine/ODM/MongoDB/DocumentManager.php | 746 ++++ .../ODM/MongoDB/DocumentNotFoundException.php | 35 + .../ODM/MongoDB/DocumentRepository.php | 239 ++ .../lib/Doctrine/ODM/MongoDB/EagerCursor.php | 224 ++ .../ODM/MongoDB/Event/LifecycleEventArgs.php | 62 + .../Event/LoadClassMetadataEventArgs.php | 54 + .../ODM/MongoDB/Event/OnClearEventArgs.php | 83 + .../ODM/MongoDB/Event/OnFlushEventArgs.php | 50 + .../ODM/MongoDB/Event/PostFlushEventArgs.php | 60 + .../ODM/MongoDB/Event/PreFlushEventArgs.php | 49 + .../ODM/MongoDB/Event/PreLoadEventArgs.php | 77 + .../ODM/MongoDB/Event/PreUpdateEventArgs.php | 113 + .../lib/Doctrine/ODM/MongoDB/Events.php | 166 + .../MongoDB/Hydrator/HydratorException.php | 46 + .../ODM/MongoDB/Hydrator/HydratorFactory.php | 419 ++ .../MongoDB/Hydrator/HydratorInterface.php | 41 + .../ODM/MongoDB/Id/AbstractIdGenerator.php | 40 + .../ODM/MongoDB/Id/AlnumGenerator.php | 100 + .../Doctrine/ODM/MongoDB/Id/AutoGenerator.php | 38 + .../ODM/MongoDB/Id/IncrementGenerator.php | 75 + .../Doctrine/ODM/MongoDB/Id/UuidGenerator.php | 158 + .../Internal/CommitOrderCalculator.php | 130 + .../Doctrine/ODM/MongoDB/LockException.php | 74 + .../lib/Doctrine/ODM/MongoDB/LockMode.php | 38 + .../Doctrine/ODM/MongoDB/LoggableCursor.php | 73 + .../Mapping/Annotations/AbstractDocument.php | 26 + .../Mapping/Annotations/AbstractField.php | 31 + .../Mapping/Annotations/AbstractIndex.php | 36 + .../MongoDB/Mapping/Annotations/AlsoLoad.php | 28 + .../ODM/MongoDB/Mapping/Annotations/Bin.php | 26 + .../MongoDB/Mapping/Annotations/BinCustom.php | 26 + .../MongoDB/Mapping/Annotations/BinFunc.php | 26 + .../MongoDB/Mapping/Annotations/BinMD5.php | 26 + .../MongoDB/Mapping/Annotations/BinUUID.php | 26 + .../MongoDB/Mapping/Annotations/Boolean.php | 26 + .../Annotations/ChangeTrackingPolicy.php | 27 + .../Mapping/Annotations/Collection.php | 27 + .../ODM/MongoDB/Mapping/Annotations/Date.php | 26 + .../Annotations/DiscriminatorField.php | 29 + .../Mapping/Annotations/DiscriminatorMap.php | 27 + .../Annotations/DiscriminatorValue.php | 27 + .../MongoDB/Mapping/Annotations/Distance.php | 26 + .../Annotations/DoctrineAnnotations.php | 74 + .../MongoDB/Mapping/Annotations/Document.php | 31 + .../MongoDB/Mapping/Annotations/EmbedMany.php | 31 + .../MongoDB/Mapping/Annotations/EmbedOne.php | 30 + .../Mapping/Annotations/EmbeddedDocument.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Field.php | 25 + .../ODM/MongoDB/Mapping/Annotations/File.php | 27 + .../ODM/MongoDB/Mapping/Annotations/Float.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Hash.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Id.php | 28 + .../MongoDB/Mapping/Annotations/Increment.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Index.php | 25 + .../MongoDB/Mapping/Annotations/Indexes.php | 27 + .../Mapping/Annotations/Inheritance.php | 30 + .../Mapping/Annotations/InheritanceType.php | 27 + .../ODM/MongoDB/Mapping/Annotations/Int.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Key.php | 26 + .../ODM/MongoDB/Mapping/Annotations/Lock.php | 27 + .../Mapping/Annotations/MappedSuperclass.php | 25 + .../MongoDB/Mapping/Annotations/NotSaved.php | 26 + .../MongoDB/Mapping/Annotations/ObjectId.php | 26 + .../MongoDB/Mapping/Annotations/PostLoad.php | 27 + .../Mapping/Annotations/PostPersist.php | 27 + .../Mapping/Annotations/PostRemove.php | 27 + .../Mapping/Annotations/PostUpdate.php | 27 + .../MongoDB/Mapping/Annotations/PreFlush.php | 27 + .../MongoDB/Mapping/Annotations/PreLoad.php | 27 + .../Mapping/Annotations/PrePersist.php | 27 + .../MongoDB/Mapping/Annotations/PreRemove.php | 27 + .../MongoDB/Mapping/Annotations/PreUpdate.php | 27 + .../ODM/MongoDB/Mapping/Annotations/Raw.php | 26 + .../Mapping/Annotations/ReferenceMany.php | 40 + .../Mapping/Annotations/ReferenceOne.php | 39 + .../MongoDB/Mapping/Annotations/String.php | 26 + .../MongoDB/Mapping/Annotations/Timestamp.php | 26 + .../Mapping/Annotations/UniqueIndex.php | 26 + .../MongoDB/Mapping/Annotations/Version.php | 27 + .../ODM/MongoDB/Mapping/ClassMetadata.php | 202 + .../MongoDB/Mapping/ClassMetadataFactory.php | 307 ++ .../ODM/MongoDB/Mapping/ClassMetadataInfo.php | 1674 ++++++++ .../Mapping/Driver/AnnotationDriver.php | 232 ++ .../ODM/MongoDB/Mapping/Driver/XmlDriver.php | 335 ++ .../ODM/MongoDB/Mapping/Driver/YamlDriver.php | 258 ++ .../ODM/MongoDB/Mapping/MappingException.php | 116 + .../Mapping/Types/BinDataCustomType.php | 50 + .../MongoDB/Mapping/Types/BinDataFuncType.php | 50 + .../MongoDB/Mapping/Types/BinDataMD5Type.php | 50 + .../ODM/MongoDB/Mapping/Types/BinDataType.php | 50 + .../MongoDB/Mapping/Types/BinDataUUIDType.php | 50 + .../ODM/MongoDB/Mapping/Types/BooleanType.php | 50 + .../MongoDB/Mapping/Types/CollectionType.php | 41 + .../MongoDB/Mapping/Types/CustomIdType.php | 51 + .../ODM/MongoDB/Mapping/Types/DateType.php | 80 + .../ODM/MongoDB/Mapping/Types/FileType.php | 31 + .../ODM/MongoDB/Mapping/Types/FloatType.php | 50 + .../ODM/MongoDB/Mapping/Types/HashType.php | 41 + .../ODM/MongoDB/Mapping/Types/IdType.php | 56 + .../MongoDB/Mapping/Types/IncrementType.php | 40 + .../ODM/MongoDB/Mapping/Types/IntType.php | 50 + .../ODM/MongoDB/Mapping/Types/KeyType.php | 46 + .../MongoDB/Mapping/Types/ObjectIdType.php | 56 + .../ODM/MongoDB/Mapping/Types/RawType.php | 51 + .../ODM/MongoDB/Mapping/Types/StringType.php | 50 + .../MongoDB/Mapping/Types/TimestampType.php | 44 + .../ODM/MongoDB/Mapping/Types/Type.php | 218 ++ .../Doctrine/ODM/MongoDB/MongoDBException.php | 77 + .../ODM/MongoDB/PersistentCollection.php | 680 ++++ .../Persisters/CollectionPersister.php | 259 ++ .../MongoDB/Persisters/DocumentPersister.php | 1031 +++++ .../MongoDB/Persisters/PersistenceBuilder.php | 421 ++ .../lib/Doctrine/ODM/MongoDB/Proxy/Proxy.php | 32 + .../ODM/MongoDB/Proxy/ProxyException.php | 49 + .../ODM/MongoDB/Proxy/ProxyFactory.php | 412 ++ .../Doctrine/ODM/MongoDB/Query/Builder.php | 312 ++ .../lib/Doctrine/ODM/MongoDB/Query/Expr.php | 131 + .../ODM/MongoDB/Query/FieldExtractor.php | 96 + .../ODM/MongoDB/Query/Filter/BsonFilter.php | 95 + .../ODM/MongoDB/Query/FilterCollection.php | 169 + .../lib/Doctrine/ODM/MongoDB/Query/Query.php | 294 ++ .../Doctrine/ODM/MongoDB/SchemaManager.php | 431 +++ .../Command/ClearCache/MetadataCommand.php | 80 + .../Command/GenerateDocumentsCommand.php | 159 + .../Command/GenerateHydratorsCommand.php | 109 + .../Command/GenerateProxiesCommand.php | 109 + .../Command/GenerateRepositoriesCommand.php | 111 + .../Tools/Console/Command/QueryCommand.php | 110 + .../Command/Schema/AbstractCommand.php | 64 + .../Console/Command/Schema/CreateCommand.php | 125 + .../Console/Command/Schema/DropCommand.php | 108 + .../Console/Command/Schema/UpdateCommand.php | 98 + .../Console/Helper/DocumentManagerHelper.php | 43 + .../MongoDB/Tools/Console/MetadataFilter.php | 75 + .../DisconnectedClassMetadataFactory.php | 68 + .../ODM/MongoDB/Tools/DocumentGenerator.php | 936 +++++ .../Tools/DocumentRepositoryGenerator.php | 78 + .../lib/Doctrine/ODM/MongoDB/UnitOfWork.php | 2782 ++++++++++++++ .../lib/Doctrine/ODM/MongoDB/Version.php | 50 + vendor/doctrine/mongodb-odm/phpunit.xml.dist | 24 + .../Doctrine/ODM/MongoDB/Tests/BaseTest.php | 82 + .../Tests/CommitOrderCalculatorTest.php | 54 + .../ODM/MongoDB/Tests/DocumentManagerTest.php | 149 + .../Tests/Events/LifecycleCallbacksTest.php | 299 ++ .../Tests/Events/LifecycleListenersTest.php | 223 ++ .../MongoDB/Tests/Functional/BinDataTest.php | 67 + .../Functional/CollectionPersisterTest.php | 328 ++ .../Tests/Functional/CollectionsTest.php | 110 + .../Tests/Functional/CursorHydrationTest.php | 36 + .../Tests/Functional/CustomFieldNameTest.php | 108 + .../MongoDB/Tests/Functional/CustomIdTest.php | 149 + .../Tests/Functional/CustomTypeTest.php | 112 + .../Tests/Functional/DatabasesTest.php | 70 + .../ODM/MongoDB/Tests/Functional/DateTest.php | 98 + .../Tests/Functional/DetachedDocumentTest.php | 128 + .../Tests/Functional/EagerCursorTest.php | 82 + .../Tests/Functional/EcommerceTest.php | 133 + .../Functional/EmbeddedReferenceTest.php | 109 + .../MongoDB/Tests/Functional/EmbeddedTest.php | 461 +++ .../MongoDB/Tests/Functional/FilesTest.php | 164 + .../MongoDB/Tests/Functional/FilterTest.php | 292 ++ .../Tests/Functional/FindAndModifyTest.php | 62 + .../Tests/Functional/FlushOptionsTest.php | 19 + .../MongoDB/Tests/Functional/FlushTest.php | 180 + .../Tests/Functional/FunctionalTest.php | 899 +++++ .../Tests/Functional/GeoSpatialTest.php | 201 + .../ODM/MongoDB/Tests/Functional/IdTest.php | 220 ++ .../Tests/Functional/IdentifiersTest.php | 101 + .../MongoDB/Tests/Functional/IndexesTest.php | 362 ++ .../Tests/Functional/InheritanceTest.php | 109 + .../Tests/Functional/LifecycleTest.php | 159 + .../ODM/MongoDB/Tests/Functional/LockTest.php | 390 ++ .../Tests/Functional/MapReduceTest.php | 257 ++ .../Tests/Functional/MappedSuperclassTest.php | 143 + .../Tests/Functional/NestedDocumentsTest.php | 292 ++ .../OwningAndInverseReferencesTest.php | 259 ++ .../PersistentCollectionCloneTest.php | 117 + .../Tests/Functional/PersistingTest.php | 86 + .../Tests/Functional/PrePersistTest.php | 51 + .../MongoDB/Tests/Functional/PrimeTest.php | 58 + .../MongoDB/Tests/Functional/QueryTest.php | 405 ++ .../MongoDB/Tests/Functional/RawTypeTest.php | 47 + .../ReferenceDiscriminatorsTest.php | 225 ++ .../ReferenceEmbeddedDocumentsTest.php | 66 + .../ReferenceRepositoryMethodTest.php | 70 + .../Tests/Functional/ReferencesTest.php | 216 ++ .../MongoDB/Tests/Functional/RemoveTest.php | 64 + .../Tests/Functional/RepositoriesTest.php | 50 + .../Tests/Functional/RequireIndexesTest.php | 315 ++ .../Tests/Functional/SimpleReferencesTest.php | 118 + .../MongoDB/Tests/Functional/SimpleTest.php | 30 + .../Tests/Functional/SlaveOkayTest.php | 93 + .../Tests/Functional/TestTargetDocument.php | 34 + .../Tests/Functional/Ticket/GH232Test.php | 80 + .../Tests/Functional/Ticket/GH245Test.php | 46 + .../Tests/Functional/Ticket/GH267Test.php | 158 + .../Tests/Functional/Ticket/GH385Test.php | 30 + .../Tests/Functional/Ticket/GH389Test.php | 73 + .../Tests/Functional/Ticket/GH426Test.php | 58 + .../Tests/Functional/Ticket/GH435Test.php | 47 + .../Tests/Functional/Ticket/GH453Test.php | 278 ++ .../Tests/Functional/Ticket/GH467Test.php | 55 + .../Tests/Functional/Ticket/MODM116Test.php | 77 + .../Tests/Functional/Ticket/MODM117Test.php | 44 + .../Tests/Functional/Ticket/MODM140Test.php | 181 + .../Tests/Functional/Ticket/MODM160Test.php | 59 + .../Tests/Functional/Ticket/MODM166Test.php | 63 + .../Tests/Functional/Ticket/MODM167Test.php | 50 + .../Tests/Functional/Ticket/MODM29Test.php | 72 + .../Tests/Functional/Ticket/MODM42/test1.txt | 0 .../Tests/Functional/Ticket/MODM42/test2.txt | 0 .../Tests/Functional/Ticket/MODM42Test.php | 86 + .../Tests/Functional/Ticket/MODM43Test.php | 42 + .../Tests/Functional/Ticket/MODM45Test.php | 45 + .../Tests/Functional/Ticket/MODM46Test.php | 41 + .../Tests/Functional/Ticket/MODM47Test.php | 33 + .../Tests/Functional/Ticket/MODM48Test.php | 52 + .../Tests/Functional/Ticket/MODM50/test.txt | 1 + .../Tests/Functional/Ticket/MODM50Test.php | 42 + .../Tests/Functional/Ticket/MODM52Test.php | 77 + .../Tests/Functional/Ticket/MODM56Test.php | 72 + .../Tests/Functional/Ticket/MODM62Test.php | 35 + .../Tests/Functional/Ticket/MODM65Test.php | 65 + .../Tests/Functional/Ticket/MODM66Test.php | 99 + .../Tests/Functional/Ticket/MODM67Test.php | 127 + .../Tests/Functional/Ticket/MODM70Test.php | 142 + .../Tests/Functional/Ticket/MODM72Test.php | 24 + .../Tests/Functional/Ticket/MODM76Test.php | 84 + .../Tests/Functional/Ticket/MODM81Test.php | 158 + .../Tests/Functional/Ticket/MODM83Test.php | 90 + .../Tests/Functional/Ticket/MODM88Test.php | 31 + .../Tests/Functional/Ticket/MODM90Test.php | 115 + .../Tests/Functional/Ticket/MODM91Test.php | 72 + .../Tests/Functional/Ticket/MODM92Test.php | 83 + .../Tests/Functional/Ticket/MODM95Test.php | 89 + .../ODM/MongoDB/Tests/Functional/file.txt | 1 + .../ODM/MongoDB/Tests/HydratorTest.php | 98 + .../Mapping/AbstractMappingDriverTest.php | 444 +++ .../Tests/Mapping/AnnotationDriverTest.php | 119 + .../Mapping/BasicInheritanceMappingTest.php | 112 + .../Mapping/ClassMetadataFactoryTest.php | 121 + .../Mapping/ClassMetadataLoadEventTest.php | 43 + .../Tests/Mapping/ClassMetadataTest.php | 190 + .../Documents/GlobalNamespaceDocument.php | 57 + .../Mapping/Driver/AbstractDriverTest.php | 234 ++ .../Tests/Mapping/Driver/XmlDriverTest.php | 16 + .../Tests/Mapping/Driver/YamlDriverTest.php | 20 + .../Driver/fixtures/EmbeddedDocument.php | 10 + .../Tests/Mapping/Driver/fixtures/User.php | 118 + .../TestDocuments.EmbeddedDocument.dcm.xml | 12 + .../fixtures/xml/TestDocuments.User.dcm.xml | 38 + .../TestDocuments.EmbeddedDocument.dcm.yml | 4 + .../fixtures/yaml/TestDocuments.User.dcm.yml | 38 + .../Tests/Mapping/XmlMappingDriverTest.php | 13 + .../Tests/Mapping/YamlMappingDriverTest.php | 72 + ...ine.ODM.MongoDB.Tests.Mapping.User.dcm.xml | 60 + ...ongoDB.Tests.Mapping.AlternateUser.dcm.yml | 29 + ...ine.ODM.MongoDB.Tests.Mapping.User.dcm.yml | 66 + .../MongoDB/Tests/Mocks/ClassMetadataMock.php | 7 + .../MongoDB/Tests/Mocks/ConnectionMock.php | 7 + .../Tests/Mocks/DocumentManagerMock.php | 114 + .../Tests/Mocks/DocumentPersisterMock.php | 87 + .../Tests/Mocks/MetadataDriverMock.php | 24 + .../Tests/Mocks/PreUpdateListenerMock.php | 31 + .../MongoDB/Tests/Mocks/UnitOfWorkMock.php | 58 + .../ODM/MongoDB/Tests/MongoCollectionTest.php | 15 + .../Tests/PersistentCollectionTest.php | 50 + .../Tests/Persisters/DereferenceTest.php | 66 + .../Persisters/PersistenceBuilderTest.php | 299 ++ .../ODM/MongoDB/Tests/Query/ExprTest.php | 222 ++ .../Tests/Query/FieldExtractorTest.php | 88 + .../Tests/Query/Filter/BsonFilterTest.php | 22 + .../ODM/MongoDB/Tests/Query/Filter/Filter.php | 16 + .../Tests/Query/FilterCollectionTest.php | 85 + .../Doctrine/ODM/MongoDB/Tests/QueryTest.php | 218 ++ .../ODM/MongoDB/Tests/SchemaManagerTest.php | 331 ++ .../Tests/Tools/DocumentGeneratorTest.php | 189 + .../ODM/MongoDB/Tests/UnitOfWorkTest.php | 513 +++ .../mongodb-odm/tests/Documents/Account.php | 44 + .../mongodb-odm/tests/Documents/Address.php | 80 + .../mongodb-odm/tests/Documents/Agent.php | 20 + .../mongodb-odm/tests/Documents/Album.php | 48 + .../mongodb-odm/tests/Documents/Article.php | 81 + .../mongodb-odm/tests/Documents/Bars/Bar.php | 48 + .../tests/Documents/Bars/Location.php | 32 + .../tests/Documents/BaseCategory.php | 45 + .../tests/Documents/BaseDocument.php | 30 + .../tests/Documents/BaseEmployee.php | 121 + .../mongodb-odm/tests/Documents/BlogPost.php | 70 + .../tests/Documents/BrowseNode.php | 37 + .../mongodb-odm/tests/Documents/Cart.php | 20 + .../mongodb-odm/tests/Documents/Category.php | 12 + .../tests/Documents/CmsAddress.php | 66 + .../tests/Documents/CmsArticle.php | 44 + .../tests/Documents/CmsComment.php | 39 + .../tests/Documents/CmsContent.php | 16 + .../mongodb-odm/tests/Documents/CmsGroup.php | 40 + .../mongodb-odm/tests/Documents/CmsPage.php | 24 + .../tests/Documents/CmsPhonenumber.php | 27 + .../tests/Documents/CmsProduct.php | 16 + .../mongodb-odm/tests/Documents/CmsUser.php | 121 + .../mongodb-odm/tests/Documents/Comment.php | 38 + .../tests/Documents/CommentRepository.php | 22 + .../Documents/CustomRepository/Document.php | 19 + .../Documents/CustomRepository/Repository.php | 10 + .../tests/Documents/CustomUser.php | 62 + .../mongodb-odm/tests/Documents/Customer.php | 23 + .../mongodb-odm/tests/Documents/Developer.php | 44 + .../Ecommerce/ConfigurableProduct.php | 143 + .../tests/Documents/Ecommerce/Currency.php | 79 + .../tests/Documents/Ecommerce/Money.php | 46 + .../tests/Documents/Ecommerce/Option.php | 77 + .../tests/Documents/Ecommerce/StockItem.php | 81 + .../mongodb-odm/tests/Documents/Employee.php | 23 + .../mongodb-odm/tests/Documents/Event.php | 56 + .../mongodb-odm/tests/Documents/Feature.php | 25 + .../mongodb-odm/tests/Documents/File.php | 50 + .../tests/Documents/ForumAvatar.php | 12 + .../mongodb-odm/tests/Documents/ForumUser.php | 38 + .../tests/Documents/FriendUser.php | 38 + .../tests/Documents/Functional/AlsoLoad.php | 65 + .../tests/Documents/Functional/Embedded.php | 12 + .../Functional/EmbeddedTestLevel0.php | 16 + .../Functional/EmbeddedTestLevel0b.php | 18 + .../Functional/EmbeddedTestLevel1.php | 43 + .../Functional/EmbeddedTestLevel2.php | 41 + .../Documents/Functional/FavoritesUser.php | 85 + .../Functional/NotAnnotatedDocument.php | 17 + .../tests/Documents/Functional/NotSaved.php | 21 + .../Documents/Functional/NotSavedEmbedded.php | 15 + .../Documents/Functional/NullFieldValues.php | 15 + .../Functional/PreUpdateTestProduct.php | 30 + .../Functional/PreUpdateTestSellable.php | 27 + .../Functional/PreUpdateTestSeller.php | 30 + .../tests/Documents/Functional/Reference.php | 15 + .../Documents/Functional/SameCollection1.php | 22 + .../Documents/Functional/SameCollection2.php | 25 + .../Functional/SimpleEmbedAndReference.php | 24 + .../EmbedManyInArrayCollectionLevel0.php | 23 + .../EmbedManyInArrayCollectionLevel1.php | 21 + .../Ticket/MODM160/EmbedManyInArrayLevel0.php | 18 + .../Ticket/MODM160/EmbedManyInArrayLevel1.php | 16 + .../Ticket/MODM160/EmbedOneLevel0.php | 18 + .../Ticket/MODM160/EmbedOneLevel1.php | 16 + .../Ticket/MODM160/MODM160Level2.php | 14 + .../Documents/Functional/VirtualHost.php | 44 + .../Functional/VirtualHostDirective.php | 118 + .../mongodb-odm/tests/Documents/Group.php | 36 + .../tests/Documents/GuestServer.php | 10 + .../mongodb-odm/tests/Documents/Issue.php | 37 + .../mongodb-odm/tests/Documents/Manager.php | 22 + .../mongodb-odm/tests/Documents/Message.php | 35 + .../tests/Documents/OtherSubProject.php | 10 + .../tests/Documents/Phonenumber.php | 42 + .../mongodb-odm/tests/Documents/Product.php | 32 + .../mongodb-odm/tests/Documents/Profile.php | 57 + .../mongodb-odm/tests/Documents/Project.php | 71 + .../mongodb-odm/tests/Documents/Server.php | 23 + .../tests/Documents/SimpleReferenceUser.php | 54 + .../mongodb-odm/tests/Documents/Song.php | 27 + .../tests/Documents/SpecialUser.php | 22 + .../mongodb-odm/tests/Documents/Strategy.php | 21 + .../tests/Documents/SubCategory.php | 10 + .../tests/Documents/SubProject.php | 25 + .../mongodb-odm/tests/Documents/Tag.php | 28 + .../mongodb-odm/tests/Documents/Task.php | 35 + .../mongodb-odm/tests/Documents/User.php | 283 ++ .../tests/Documents/UserUpsert.php | 33 + .../tests/Documents/UserUpsertChild.php | 13 + .../Documents/UserUpsertIdStrategyNone.php | 32 + .../mongodb-odm/tests/NativePhpunitTask.php | 238 ++ .../tests/Stubs/DocumentManager.php | 42 + .../doctrine/mongodb-odm/tests/bootstrap.php | 13 + .../tools/sandbox/Documents/Account.php | 40 + .../tools/sandbox/Documents/Address.php | 61 + .../tools/sandbox/Documents/Phonenumber.php | 32 + .../tools/sandbox/Documents/User.php | 87 + .../sandbox/Documents/UserRepository.php | 15 + .../mongodb-odm/tools/sandbox/cli-config.php | 7 + .../mongodb-odm/tools/sandbox/config.php | 36 + .../mongodb-odm/tools/sandbox/index.php | 10 + .../mongodb-odm/tools/sandbox/mongodb | 4 + .../mongodb-odm/tools/sandbox/mongodb.php | 26 + vendor/doctrine/mongodb/.gitignore | 5 + vendor/doctrine/mongodb/.travis.yml | 21 + vendor/doctrine/mongodb/LICENSE | 8 + vendor/doctrine/mongodb/README.markdown | 12 + vendor/doctrine/mongodb/composer.json | 22 + .../lib/Doctrine/MongoDB/ArrayIterator.php | 120 + .../lib/Doctrine/MongoDB/Collection.php | 699 ++++ .../lib/Doctrine/MongoDB/Configuration.php | 121 + .../lib/Doctrine/MongoDB/Connection.php | 308 ++ .../mongodb/lib/Doctrine/MongoDB/Cursor.php | 422 ++ .../mongodb/lib/Doctrine/MongoDB/Database.php | 320 ++ .../lib/Doctrine/MongoDB/EagerCursor.php | 113 + .../Event/CreateCollectionEventArgs.php | 73 + .../MongoDB/Event/DistinctEventArgs.php | 54 + .../lib/Doctrine/MongoDB/Event/EventArgs.php | 52 + .../Doctrine/MongoDB/Event/GroupEventArgs.php | 61 + .../MongoDB/Event/MapReduceEventArgs.php | 68 + .../Doctrine/MongoDB/Event/NearEventArgs.php | 59 + .../MongoDB/Event/UpdateEventArgs.php | 59 + .../mongodb/lib/Doctrine/MongoDB/Events.php | 103 + .../mongodb/lib/Doctrine/MongoDB/GridFS.php | 231 ++ .../lib/Doctrine/MongoDB/GridFSFile.php | 228 ++ .../mongodb/lib/Doctrine/MongoDB/Iterator.php | 34 + .../Doctrine/MongoDB/IteratorAggregate.php | 35 + .../mongodb/lib/Doctrine/MongoDB/Loggable.php | 38 + .../Doctrine/MongoDB/LoggableCollection.php | 259 ++ .../lib/Doctrine/MongoDB/LoggableCursor.php | 165 + .../lib/Doctrine/MongoDB/LoggableDatabase.php | 159 + .../lib/Doctrine/MongoDB/Query/Builder.php | 1118 ++++++ .../lib/Doctrine/MongoDB/Query/Expr.php | 399 ++ .../lib/Doctrine/MongoDB/Query/Query.php | 295 ++ .../Doctrine/MongoDB/Util/ReadPreference.php | 108 + vendor/doctrine/mongodb/phpunit.xml.dist | 24 + .../tests/Doctrine/MongoDB/Tests/BaseTest.php | 32 + .../Doctrine/MongoDB/Tests/CollectionTest.php | 572 +++ .../Doctrine/MongoDB/Tests/ConnectionTest.php | 200 + .../Constraint/ArrayHasValueUnderKey.php | 43 + .../MongoDB/Tests/CursorFunctionalTest.php | 44 + .../Doctrine/MongoDB/Tests/CursorTest.php | 212 ++ .../Doctrine/MongoDB/Tests/DatabaseTest.php | 130 + .../MongoDB/Tests/EagerCursorTest.php | 60 + .../Doctrine/MongoDB/Tests/EventTest.php | 26 + .../Doctrine/MongoDB/Tests/FunctionalTest.php | 124 + .../Doctrine/MongoDB/Tests/GridFSFileTest.php | 164 + .../Doctrine/MongoDB/Tests/GridFSTest.php | 164 + .../MongoDB/Tests/LoggableCursorTest.php | 28 + .../MongoDB/Tests/Query/BuilderTest.php | 529 +++ .../MongoDB/Tests/Query/QueryTest.php | 101 + .../Doctrine/MongoDB/Tests/RetryTest.php | 165 + .../MongoDB/Tests/Util/ReadPreferenceTest.php | 98 + .../tests/Doctrine/MongoDB/Tests/file.txt | 1 + vendor/doctrine/mongodb/tests/bootstrap.php | 9 + vendor/doctrine/orm/LICENSE | 19 + vendor/doctrine/orm/README.markdown | 20 + vendor/doctrine/orm/UPGRADE.md | 525 +++ vendor/doctrine/orm/bin/doctrine | 4 + vendor/doctrine/orm/bin/doctrine-pear.php | 50 + vendor/doctrine/orm/bin/doctrine.bat | 9 + vendor/doctrine/orm/bin/doctrine.php | 59 + vendor/doctrine/orm/composer.json | 37 + vendor/doctrine/orm/docs/README.md | 8 + vendor/doctrine/orm/docs/bin/generate-docs.sh | 10 + .../orm/docs/bin/install-dependencies.sh | 4 + vendor/doctrine/orm/docs/en/Makefile | 89 + .../orm/docs/en/_exts/configurationblock.py | 93 + vendor/doctrine/orm/docs/en/conf.py | 201 + ...-conversion-using-custom-mapping-types.rst | 256 ++ .../orm/docs/en/cookbook/aggregate-fields.rst | 376 ++ .../docs/en/cookbook/decorator-pattern.rst | 273 ++ .../docs/en/cookbook/dql-custom-walkers.rst | 217 ++ .../cookbook/dql-user-defined-functions.rst | 240 ++ .../docs/en/cookbook/entities-in-session.rst | 68 + ...menting-arrayaccess-for-domain-objects.rst | 112 + ...nting-the-notify-changetracking-policy.rst | 72 + .../cookbook/implementing-wakeup-or-clone.rst | 78 + .../cookbook/integrating-with-codeigniter.rst | 140 + .../orm/docs/en/cookbook/mysql-enums.rst | 189 + .../resolve-target-entity-listener.rst | 137 + .../docs/en/cookbook/sql-table-prefixes.rst | 80 + .../strategy-cookbook-introduction.rst | 254 ++ .../en/cookbook/validation-of-entities.rst | 137 + .../en/cookbook/working-with-datetime.rst | 168 + vendor/doctrine/orm/docs/en/index.rst | 122 + vendor/doctrine/orm/docs/en/make.bat | 113 + .../en/reference/advanced-configuration.rst | 432 +++ .../en/reference/annotations-reference.rst | 1114 ++++++ .../orm/docs/en/reference/architecture.rst | 197 + .../docs/en/reference/association-mapping.rst | 1145 ++++++ .../orm/docs/en/reference/basic-mapping.rst | 683 ++++ .../docs/en/reference/batch-processing.rst | 179 + .../orm/docs/en/reference/best-practices.rst | 127 + .../orm/docs/en/reference/caching.rst | 439 +++ .../en/reference/change-tracking-policies.rst | 151 + .../orm/docs/en/reference/configuration.rst | 140 + .../reference/dql-doctrine-query-language.rst | 1672 ++++++++ .../doctrine/orm/docs/en/reference/events.rst | 955 +++++ vendor/doctrine/orm/docs/en/reference/faq.rst | 224 ++ .../orm/docs/en/reference/filters.rst | 93 + .../en/reference/improving-performance.rst | 64 + .../docs/en/reference/inheritance-mapping.rst | 559 +++ .../orm/docs/en/reference/installation.rst | 5 + .../limitations-and-known-issues.rst | 189 + .../docs/en/reference/metadata-drivers.rst | 194 + .../orm/docs/en/reference/namingstrategy.rst | 150 + .../orm/docs/en/reference/native-sql.rst | 905 +++++ .../orm/docs/en/reference/partial-objects.rst | 90 + .../orm/docs/en/reference/php-mapping.rst | 310 ++ .../orm/docs/en/reference/query-builder.rst | 541 +++ .../doctrine/orm/docs/en/reference/tools.rst | 509 +++ .../transactions-and-concurrency.rst | 353 ++ .../en/reference/unitofwork-associations.rst | 60 + .../orm/docs/en/reference/unitofwork.rst | 201 + .../reference/working-with-associations.rst | 712 ++++ .../en/reference/working-with-objects.rst | 850 +++++ .../orm/docs/en/reference/xml-mapping.rst | 747 ++++ .../orm/docs/en/reference/yaml-mapping.rst | 119 + vendor/doctrine/orm/docs/en/toc.rst | 80 + .../en/tutorials/composite-primary-keys.rst | 375 ++ .../en/tutorials/extra-lazy-associations.rst | 84 + .../en/tutorials/getting-started-database.rst | 27 + .../en/tutorials/getting-started-models.rst | 24 + .../orm/docs/en/tutorials/getting-started.rst | 1539 ++++++++ .../en/tutorials/ordered-associations.rst | 110 + ...eld-association-mappings-in-subclasses.rst | 90 + .../orm/docs/en/tutorials/pagination.rst | 43 + .../working-with-indexed-associations.rst | 298 ++ vendor/doctrine/orm/doctrine-mapping.xsd | 563 +++ .../orm/lib/Doctrine/ORM/AbstractQuery.php | 872 +++++ .../orm/lib/Doctrine/ORM/Configuration.php | 782 ++++ .../ORM/Decorator/EntityManagerDecorator.php | 271 ++ .../orm/lib/Doctrine/ORM/EntityManager.php | 1003 +++++ .../Doctrine/ORM/EntityManagerInterface.php | 60 + .../Doctrine/ORM/EntityNotFoundException.php | 37 + .../orm/lib/Doctrine/ORM/EntityRepository.php | 306 ++ .../Doctrine/ORM/Event/LifecycleEventArgs.php | 55 + .../Doctrine/ORM/Event/ListenersInvoker.php | 120 + .../ORM/Event/LoadClassMetadataEventArgs.php | 44 + .../Doctrine/ORM/Event/OnClearEventArgs.php | 86 + .../Doctrine/ORM/Event/OnFlushEventArgs.php | 61 + .../Doctrine/ORM/Event/PostFlushEventArgs.php | 58 + .../Doctrine/ORM/Event/PreFlushEventArgs.php | 58 + .../Doctrine/ORM/Event/PreUpdateEventArgs.php | 138 + .../doctrine/orm/lib/Doctrine/ORM/Events.php | 159 + .../Doctrine/ORM/Id/AbstractIdGenerator.php | 50 + .../lib/Doctrine/ORM/Id/AssignedGenerator.php | 75 + .../ORM/Id/BigIntegerIdentityGenerator.php | 66 + .../lib/Doctrine/ORM/Id/IdentityGenerator.php | 65 + .../lib/Doctrine/ORM/Id/SequenceGenerator.php | 136 + .../lib/Doctrine/ORM/Id/TableGenerator.php | 108 + .../orm/lib/Doctrine/ORM/Id/UuidGenerator.php | 48 + .../ORM/Internal/CommitOrderCalculator.php | 156 + .../Internal/Hydration/AbstractHydrator.php | 455 +++ .../ORM/Internal/Hydration/ArrayHydrator.php | 319 ++ .../Internal/Hydration/HydrationException.php | 91 + .../ORM/Internal/Hydration/IterableResult.php | 109 + .../ORM/Internal/Hydration/ObjectHydrator.php | 629 +++ .../ORM/Internal/Hydration/ScalarHydrator.php | 55 + .../Hydration/SimpleObjectHydrator.php | 188 + .../Hydration/SingleScalarHydrator.php | 55 + .../lib/Doctrine/ORM/Mapping/Annotation.php | 24 + .../ORM/Mapping/AssociationOverride.php | 53 + .../ORM/Mapping/AssociationOverrides.php | 39 + .../ORM/Mapping/AttributeOverride.php | 46 + .../ORM/Mapping/AttributeOverrides.php | 39 + .../Mapping/Builder/AssociationBuilder.php | 207 + .../Mapping/Builder/ClassMetadataBuilder.php | 493 +++ .../Mapping/Builder/EntityListenerBuilder.php | 72 + .../ORM/Mapping/Builder/FieldBuilder.php | 232 ++ .../Builder/ManyToManyAssociationBuilder.php | 98 + .../Builder/OneToManyAssociationBuilder.php | 67 + .../ORM/Mapping/ChangeTrackingPolicy.php | 36 + .../Doctrine/ORM/Mapping/ClassMetadata.php | 29 + .../ORM/Mapping/ClassMetadataFactory.php | 572 +++ .../ORM/Mapping/ClassMetadataInfo.php | 3029 +++++++++++++++ .../orm/lib/Doctrine/ORM/Mapping/Column.php | 76 + .../lib/Doctrine/ORM/Mapping/ColumnResult.php | 40 + .../ORM/Mapping/CustomIdGenerator.php | 32 + .../Mapping/DefaultEntityListenerResolver.php | 75 + .../ORM/Mapping/DefaultNamingStrategy.php | 86 + .../ORM/Mapping/DefaultQuoteStrategy.php | 142 + .../ORM/Mapping/DiscriminatorColumn.php | 54 + .../Doctrine/ORM/Mapping/DiscriminatorMap.php | 32 + .../ORM/Mapping/Driver/AnnotationDriver.php | 602 +++ .../ORM/Mapping/Driver/DatabaseDriver.php | 428 +++ .../Mapping/Driver/DoctrineAnnotations.php | 67 + .../ORM/Mapping/Driver/DriverChain.php | 31 + .../Doctrine/ORM/Mapping/Driver/PHPDriver.php | 31 + .../Mapping/Driver/SimplifiedXmlDriver.php | 43 + .../Mapping/Driver/SimplifiedYamlDriver.php | 43 + .../ORM/Mapping/Driver/StaticPHPDriver.php | 31 + .../Doctrine/ORM/Mapping/Driver/XmlDriver.php | 760 ++++ .../ORM/Mapping/Driver/YamlDriver.php | 710 ++++ .../ORM/Mapping/ElementCollection.php | 33 + .../orm/lib/Doctrine/ORM/Mapping/Entity.php | 37 + .../ORM/Mapping/EntityListenerResolver.php | 55 + .../Doctrine/ORM/Mapping/EntityListeners.php | 41 + .../lib/Doctrine/ORM/Mapping/EntityResult.php | 56 + .../lib/Doctrine/ORM/Mapping/FieldResult.php | 46 + .../Doctrine/ORM/Mapping/GeneratedValue.php | 36 + .../ORM/Mapping/HasLifecycleCallbacks.php | 28 + .../orm/lib/Doctrine/ORM/Mapping/Id.php | 28 + .../orm/lib/Doctrine/ORM/Mapping/Index.php | 37 + .../Doctrine/ORM/Mapping/InheritanceType.php | 36 + .../lib/Doctrine/ORM/Mapping/JoinColumn.php | 64 + .../lib/Doctrine/ORM/Mapping/JoinColumns.php | 32 + .../lib/Doctrine/ORM/Mapping/JoinTable.php | 47 + .../lib/Doctrine/ORM/Mapping/ManyToMany.php | 66 + .../lib/Doctrine/ORM/Mapping/ManyToOne.php | 51 + .../Doctrine/ORM/Mapping/MappedSuperclass.php | 32 + .../Doctrine/ORM/Mapping/MappingException.php | 760 ++++ .../ORM/Mapping/NamedNativeQueries.php | 40 + .../Doctrine/ORM/Mapping/NamedNativeQuery.php | 61 + .../lib/Doctrine/ORM/Mapping/NamedQueries.php | 32 + .../lib/Doctrine/ORM/Mapping/NamedQuery.php | 37 + .../Doctrine/ORM/Mapping/NamingStrategy.php | 88 + .../lib/Doctrine/ORM/Mapping/OneToMany.php | 61 + .../orm/lib/Doctrine/ORM/Mapping/OneToOne.php | 61 + .../orm/lib/Doctrine/ORM/Mapping/OrderBy.php | 32 + .../orm/lib/Doctrine/ORM/Mapping/PostLoad.php | 28 + .../lib/Doctrine/ORM/Mapping/PostPersist.php | 28 + .../lib/Doctrine/ORM/Mapping/PostRemove.php | 28 + .../lib/Doctrine/ORM/Mapping/PostUpdate.php | 28 + .../orm/lib/Doctrine/ORM/Mapping/PreFlush.php | 28 + .../lib/Doctrine/ORM/Mapping/PrePersist.php | 28 + .../lib/Doctrine/ORM/Mapping/PreRemove.php | 28 + .../lib/Doctrine/ORM/Mapping/PreUpdate.php | 28 + .../Doctrine/ORM/Mapping/QuoteStrategy.php | 120 + .../ORM/Mapping/SequenceGenerator.php | 42 + .../ORM/Mapping/SqlResultSetMapping.php | 54 + .../ORM/Mapping/SqlResultSetMappings.php | 40 + .../orm/lib/Doctrine/ORM/Mapping/Table.php | 52 + .../ORM/Mapping/UnderscoreNamingStrategy.php | 138 + .../Doctrine/ORM/Mapping/UniqueConstraint.php | 37 + .../orm/lib/Doctrine/ORM/Mapping/Version.php | 28 + .../orm/lib/Doctrine/ORM/NativeQuery.php | 92 + .../lib/Doctrine/ORM/NoResultException.php | 37 + .../Doctrine/ORM/NonUniqueResultException.php | 30 + .../orm/lib/Doctrine/ORM/ORMException.php | 271 ++ .../ORM/ORMInvalidArgumentException.php | 201 + .../Doctrine/ORM/OptimisticLockException.php | 88 + .../lib/Doctrine/ORM/PersistentCollection.php | 864 +++++ .../AbstractCollectionPersister.php | 322 ++ .../AbstractEntityInheritancePersister.php | 93 + .../ORM/Persisters/BasicEntityPersister.php | 1971 ++++++++++ .../Persisters/ElementCollectionPersister.php | 29 + .../Persisters/JoinedSubclassPersister.php | 560 +++ .../ORM/Persisters/ManyToManyPersister.php | 461 +++ .../ORM/Persisters/OneToManyPersister.php | 229 ++ .../ORM/Persisters/PersisterException.php | 20 + .../ORM/Persisters/SingleTablePersister.php | 185 + .../ORM/Persisters/SqlExpressionVisitor.php | 119 + .../ORM/Persisters/SqlValueVisitor.php | 118 + .../ORM/Persisters/UnionSubclassPersister.php | 24 + .../Doctrine/ORM/PessimisticLockException.php | 40 + .../orm/lib/Doctrine/ORM/Proxy/Autoloader.php | 29 + .../orm/lib/Doctrine/ORM/Proxy/Proxy.php | 32 + .../lib/Doctrine/ORM/Proxy/ProxyFactory.php | 218 ++ .../doctrine/orm/lib/Doctrine/ORM/Query.php | 662 ++++ .../Doctrine/ORM/Query/AST/ASTException.php | 38 + .../ORM/Query/AST/AggregateExpression.php | 69 + .../ORM/Query/AST/ArithmeticExpression.php | 66 + .../ORM/Query/AST/ArithmeticFactor.php | 78 + .../Doctrine/ORM/Query/AST/ArithmeticTerm.php | 53 + .../ORM/Query/AST/BetweenExpression.php | 72 + .../ORM/Query/AST/CoalesceExpression.php | 55 + .../Query/AST/CollectionMemberExpression.php | 63 + .../ORM/Query/AST/ComparisonExpression.php | 72 + .../ORM/Query/AST/ConditionalExpression.php | 53 + .../ORM/Query/AST/ConditionalFactor.php | 58 + .../ORM/Query/AST/ConditionalPrimary.php | 66 + .../ORM/Query/AST/ConditionalTerm.php | 52 + .../Doctrine/ORM/Query/AST/DeleteClause.php | 58 + .../ORM/Query/AST/DeleteStatement.php | 58 + .../EmptyCollectionComparisonExpression.php | 58 + .../ORM/Query/AST/ExistsExpression.php | 58 + .../lib/Doctrine/ORM/Query/AST/FromClause.php | 54 + .../ORM/Query/AST/Functions/AbsFunction.php | 64 + .../Query/AST/Functions/BitAndFunction.php | 63 + .../ORM/Query/AST/Functions/BitOrFunction.php | 63 + .../Query/AST/Functions/ConcatFunction.php | 83 + .../AST/Functions/CurrentDateFunction.php | 54 + .../AST/Functions/CurrentTimeFunction.php | 54 + .../Functions/CurrentTimestampFunction.php | 54 + .../Query/AST/Functions/DateAddFunction.php | 83 + .../Query/AST/Functions/DateDiffFunction.php | 64 + .../Query/AST/Functions/DateSubFunction.php | 60 + .../ORM/Query/AST/Functions/FunctionNode.php | 73 + .../Query/AST/Functions/IdentityFunction.php | 110 + .../Query/AST/Functions/LengthFunction.php | 61 + .../Query/AST/Functions/LocateFunction.php | 84 + .../ORM/Query/AST/Functions/LowerFunction.php | 61 + .../ORM/Query/AST/Functions/ModFunction.php | 74 + .../ORM/Query/AST/Functions/SizeFunction.php | 123 + .../ORM/Query/AST/Functions/SqrtFunction.php | 64 + .../Query/AST/Functions/SubstringFunction.php | 89 + .../ORM/Query/AST/Functions/TrimFunction.php | 115 + .../ORM/Query/AST/Functions/UpperFunction.php | 61 + .../ORM/Query/AST/GeneralCaseExpression.php | 62 + .../Doctrine/ORM/Query/AST/GroupByClause.php | 53 + .../Doctrine/ORM/Query/AST/HavingClause.php | 53 + .../AST/IdentificationVariableDeclaration.php | 67 + .../Doctrine/ORM/Query/AST/InExpression.php | 67 + .../lib/Doctrine/ORM/Query/AST/IndexBy.php | 53 + .../Doctrine/ORM/Query/AST/InputParameter.php | 66 + .../ORM/Query/AST/InstanceOfExpression.php | 64 + .../orm/lib/Doctrine/ORM/Query/AST/Join.php | 70 + .../Query/AST/JoinAssociationDeclaration.php | 65 + .../AST/JoinAssociationPathExpression.php | 60 + .../ORM/Query/AST/JoinClassPathExpression.php | 59 + .../Doctrine/ORM/Query/AST/LikeExpression.php | 71 + .../lib/Doctrine/ORM/Query/AST/Literal.php | 55 + .../ORM/Query/AST/NewObjectExpression.php | 58 + .../orm/lib/Doctrine/ORM/Query/AST/Node.php | 103 + .../Query/AST/NullComparisonExpression.php | 58 + .../ORM/Query/AST/NullIfExpression.php | 62 + .../Doctrine/ORM/Query/AST/OrderByClause.php | 53 + .../Doctrine/ORM/Query/AST/OrderByItem.php | 75 + .../ORM/Query/AST/ParenthesisExpression.php | 51 + .../ORM/Query/AST/PartialObjectExpression.php | 43 + .../Doctrine/ORM/Query/AST/PathExpression.php | 81 + .../ORM/Query/AST/QuantifiedExpression.php | 81 + .../Query/AST/RangeVariableDeclaration.php | 60 + .../Doctrine/ORM/Query/AST/SelectClause.php | 60 + .../ORM/Query/AST/SelectExpression.php | 68 + .../ORM/Query/AST/SelectStatement.php | 80 + .../Query/AST/SimpleArithmeticExpression.php | 53 + .../ORM/Query/AST/SimpleCaseExpression.php | 69 + .../ORM/Query/AST/SimpleSelectClause.php | 60 + .../ORM/Query/AST/SimpleSelectExpression.php | 59 + .../ORM/Query/AST/SimpleWhenClause.php | 62 + .../lib/Doctrine/ORM/Query/AST/Subselect.php | 80 + .../ORM/Query/AST/SubselectFromClause.php | 53 + .../Doctrine/ORM/Query/AST/UpdateClause.php | 65 + .../lib/Doctrine/ORM/Query/AST/UpdateItem.php | 62 + .../ORM/Query/AST/UpdateStatement.php | 58 + .../lib/Doctrine/ORM/Query/AST/WhenClause.php | 62 + .../Doctrine/ORM/Query/AST/WhereClause.php | 53 + .../ORM/Query/Exec/AbstractSqlExecutor.php | 76 + .../Query/Exec/MultiTableDeleteExecutor.php | 145 + .../Query/Exec/MultiTableUpdateExecutor.php | 206 + .../ORM/Query/Exec/SingleSelectExecutor.php | 52 + .../Exec/SingleTableDeleteUpdateExecutor.php | 57 + .../orm/lib/Doctrine/ORM/Query/Expr.php | 647 ++++ .../orm/lib/Doctrine/ORM/Query/Expr/Andx.php | 55 + .../orm/lib/Doctrine/ORM/Query/Expr/Base.php | 124 + .../Doctrine/ORM/Query/Expr/Comparison.php | 100 + .../lib/Doctrine/ORM/Query/Expr/Composite.php | 71 + .../orm/lib/Doctrine/ORM/Query/Expr/From.php | 92 + .../orm/lib/Doctrine/ORM/Query/Expr/Func.php | 78 + .../lib/Doctrine/ORM/Query/Expr/GroupBy.php | 50 + .../orm/lib/Doctrine/ORM/Query/Expr/Join.php | 145 + .../lib/Doctrine/ORM/Query/Expr/Literal.php | 50 + .../orm/lib/Doctrine/ORM/Query/Expr/Math.php | 107 + .../lib/Doctrine/ORM/Query/Expr/OrderBy.php | 104 + .../orm/lib/Doctrine/ORM/Query/Expr/Orx.php | 55 + .../lib/Doctrine/ORM/Query/Expr/Select.php | 57 + .../Doctrine/ORM/Query/Filter/SQLFilter.php | 129 + .../Doctrine/ORM/Query/FilterCollection.php | 211 + .../orm/lib/Doctrine/ORM/Query/Lexer.php | 207 + .../orm/lib/Doctrine/ORM/Query/Parameter.php | 107 + .../ORM/Query/ParameterTypeInferer.php | 68 + .../orm/lib/Doctrine/ORM/Query/Parser.php | 3381 +++++++++++++++++ .../lib/Doctrine/ORM/Query/ParserResult.php | 145 + .../orm/lib/Doctrine/ORM/Query/Printer.php | 98 + .../lib/Doctrine/ORM/Query/QueryException.php | 252 ++ .../ORM/Query/QueryExpressionVisitor.php | 179 + .../Doctrine/ORM/Query/ResultSetMapping.php | 547 +++ .../ORM/Query/ResultSetMappingBuilder.php | 443 +++ .../orm/lib/Doctrine/ORM/Query/SqlWalker.php | 2191 +++++++++++ .../orm/lib/Doctrine/ORM/Query/TreeWalker.php | 479 +++ .../Doctrine/ORM/Query/TreeWalkerAdapter.php | 440 +++ .../Doctrine/ORM/Query/TreeWalkerChain.php | 574 +++ .../orm/lib/Doctrine/ORM/QueryBuilder.php | 1296 +++++++ .../orm/lib/Doctrine/ORM/README.markdown | 0 .../Command/ClearCache/MetadataCommand.php | 103 + .../Command/ClearCache/QueryCommand.php | 103 + .../Command/ClearCache/ResultCommand.php | 103 + .../Command/ConvertDoctrine1SchemaCommand.php | 230 ++ .../Console/Command/ConvertMappingCommand.php | 200 + .../EnsureProductionSettingsCommand.php | 81 + .../Command/GenerateEntitiesCommand.php | 164 + .../Command/GenerateProxiesCommand.php | 115 + .../Command/GenerateRepositoriesCommand.php | 117 + .../ORM/Tools/Console/Command/InfoCommand.php | 90 + .../Tools/Console/Command/RunDqlCommand.php | 123 + .../Command/SchemaTool/AbstractCommand.php | 71 + .../Command/SchemaTool/CreateCommand.php | 86 + .../Command/SchemaTool/DropCommand.php | 128 + .../Command/SchemaTool/UpdateCommand.php | 155 + .../Console/Command/ValidateSchemaCommand.php | 90 + .../ORM/Tools/Console/ConsoleRunner.php | 119 + .../Console/Helper/EntityManagerHelper.php | 71 + .../ORM/Tools/Console/MetadataFilter.php | 94 + .../ORM/Tools/ConvertDoctrine1Schema.php | 344 ++ .../ORM/Tools/DebugUnitOfWorkListener.php | 184 + .../DisconnectedClassMetadataFactory.php | 48 + .../Doctrine/ORM/Tools/EntityGenerator.php | 1504 ++++++++ .../ORM/Tools/EntityRepositoryGenerator.php | 106 + .../Tools/Event/GenerateSchemaEventArgs.php | 71 + .../Event/GenerateSchemaTableEventArgs.php | 86 + .../Tools/Export/ClassMetadataExporter.php | 78 + .../Tools/Export/Driver/AbstractExporter.php | 249 ++ .../Export/Driver/AnnotationExporter.php | 80 + .../ORM/Tools/Export/Driver/PhpExporter.php | 173 + .../ORM/Tools/Export/Driver/XmlExporter.php | 396 ++ .../ORM/Tools/Export/Driver/YamlExporter.php | 211 + .../ORM/Tools/Export/ExportException.php | 38 + .../Tools/Pagination/CountOutputWalker.php | 139 + .../ORM/Tools/Pagination/CountWalker.php | 93 + .../Pagination/LimitSubqueryOutputWalker.php | 214 ++ .../Tools/Pagination/LimitSubqueryWalker.php | 119 + .../ORM/Tools/Pagination/Paginator.php | 234 ++ .../ORM/Tools/Pagination/WhereInWalker.php | 147 + .../ORM/Tools/ResolveTargetEntityListener.php | 103 + .../orm/lib/Doctrine/ORM/Tools/SchemaTool.php | 867 +++++ .../Doctrine/ORM/Tools/SchemaValidator.php | 271 ++ .../orm/lib/Doctrine/ORM/Tools/Setup.php | 159 + .../orm/lib/Doctrine/ORM/Tools/ToolEvents.php | 42 + .../lib/Doctrine/ORM/Tools/ToolsException.php | 51 + .../ORM/TransactionRequiredException.php | 40 + .../ORM/UnexpectedResultException.php | 30 + .../orm/lib/Doctrine/ORM/UnitOfWork.php | 3206 ++++++++++++++++ .../doctrine/orm/lib/Doctrine/ORM/Version.php | 56 + .../Symfony/Component/Console/.gitignore | 4 + .../Symfony/Component/Console/Application.php | 1144 ++++++ .../Symfony/Component/Console/CHANGELOG.md | 27 + .../Component/Console/Command/Command.php | 638 ++++ .../Component/Console/Command/HelpCommand.php | 85 + .../Component/Console/Command/ListCommand.php | 86 + .../Console/Formatter/OutputFormatter.php | 248 ++ .../Formatter/OutputFormatterInterface.php | 83 + .../Formatter/OutputFormatterStyle.php | 222 ++ .../OutputFormatterStyleInterface.php | 72 + .../Formatter/OutputFormatterStyleStack.php | 121 + .../Component/Console/Helper/DialogHelper.php | 444 +++ .../Console/Helper/FormatterHelper.php | 100 + .../Component/Console/Helper/Helper.php | 42 + .../Console/Helper/HelperInterface.php | 49 + .../Component/Console/Helper/HelperSet.php | 104 + .../Console/Helper/ProgressHelper.php | 411 ++ .../Component/Console/Input/ArgvInput.php | 315 ++ .../Component/Console/Input/ArrayInput.php | 190 + .../Symfony/Component/Console/Input/Input.php | 213 ++ .../Component/Console/Input/InputArgument.php | 132 + .../Console/Input/InputDefinition.php | 535 +++ .../Console/Input/InputInterface.php | 152 + .../Component/Console/Input/InputOption.php | 209 + .../Component/Console/Input/StringInput.php | 87 + .../console/Symfony/Component/Console/LICENSE | 19 + .../Console/Output/ConsoleOutput.php | 103 + .../Console/Output/ConsoleOutputInterface.php | 30 + .../Component/Console/Output/NullOutput.php | 34 + .../Component/Console/Output/Output.php | 180 + .../Console/Output/OutputInterface.php | 109 + .../Component/Console/Output/StreamOutput.php | 113 + .../Symfony/Component/Console/README.md | 63 + .../Console/Resources/bin/hiddeninput.exe | Bin 0 -> 9216 bytes .../Symfony/Component/Console/Shell.php | 211 + .../Console/Tester/ApplicationTester.php | 109 + .../Console/Tester/CommandTester.php | 102 + .../Console/Tests/ApplicationTest.php | 660 ++++ .../Console/Tests/Command/CommandTest.php | 285 ++ .../Console/Tests/Command/HelpCommandTest.php | 50 + .../Console/Tests/Command/ListCommandTest.php | 38 + .../Console/Tests/Fixtures/Foo1Command.php | 26 + .../Console/Tests/Fixtures/Foo2Command.php | 21 + .../Console/Tests/Fixtures/Foo3Command.php | 25 + .../Console/Tests/Fixtures/Foo4Command.php | 11 + .../Console/Tests/Fixtures/FooCommand.php | 33 + .../Console/Tests/Fixtures/TestCommand.php | 28 + .../Tests/Fixtures/application_astext1.txt | 20 + .../Tests/Fixtures/application_astext2.txt | 16 + .../Tests/Fixtures/application_asxml1.txt | 128 + .../Tests/Fixtures/application_asxml2.txt | 37 + .../Tests/Fixtures/application_gethelp.txt | 13 + .../Fixtures/application_renderexception1.txt | 8 + .../Fixtures/application_renderexception2.txt | 11 + .../Fixtures/application_renderexception3.txt | 19 + .../Fixtures/application_renderexception4.txt | 9 + .../Tests/Fixtures/application_run1.txt | 17 + .../Tests/Fixtures/application_run2.txt | 28 + .../Tests/Fixtures/application_run3.txt | 27 + .../Tests/Fixtures/application_run4.txt | 1 + .../Console/Tests/Fixtures/command_astext.txt | 18 + .../Console/Tests/Fixtures/command_asxml.txt | 38 + .../Tests/Fixtures/definition_astext.txt | 11 + .../Tests/Fixtures/definition_asxml.txt | 39 + .../OutputFormatterStyleStackTest.php | 70 + .../Formatter/OutputFormatterStyleTest.php | 77 + .../Tests/Formatter/OutputFormatterTest.php | 231 ++ .../Console/Tests/Helper/DialogHelperTest.php | 167 + .../Tests/Helper/FormatterHelperTest.php | 84 + .../Tests/Helper/ProgressHelperTest.php | 96 + .../Console/Tests/Input/ArgvInputTest.php | 229 ++ .../Console/Tests/Input/ArrayInputTest.php | 90 + .../Console/Tests/Input/InputArgumentTest.php | 104 + .../Tests/Input/InputDefinitionTest.php | 363 ++ .../Console/Tests/Input/InputOptionTest.php | 192 + .../Console/Tests/Input/InputTest.php | 117 + .../Console/Tests/Input/StringInputTest.php | 76 + .../Tests/Output/ConsoleOutputTest.php | 24 + .../Console/Tests/Output/NullOutputTest.php | 24 + .../Console/Tests/Output/OutputTest.php | 101 + .../Console/Tests/Output/StreamOutputTest.php | 59 + .../Tests/Tester/ApplicationTesterTest.php | 64 + .../Tests/Tester/CommandTesterTest.php | 62 + .../Symfony/Component/Console/composer.json | 31 + .../Component/Console/phpunit.xml.dist | 30 + 1533 files changed, 207199 insertions(+), 1062 deletions(-) create mode 100644 config/autoload/doctrineorm.local.php.dist create mode 100644 config/autoload/local.php.appfog.dist create mode 100644 data/DoctrineMongoODMModule/Hydrator/.gitignore create mode 100644 data/DoctrineMongoODMModule/Proxy/.gitignore create mode 100644 data/DoctrineORMModule/Proxy/.gitignore create mode 100644 fmi.sql create mode 100644 localVirtualHost.conf.distribution create mode 100644 module/Fmi/config/module.config.php create mode 100644 module/Fmi/src/Fmi/Controller/IndexController.php create mode 100644 module/Fmi/src/Fmi/Entity/Album.php create mode 100644 module/Fmi/src/Fmi/Entity/Repository/UserRepository.php create mode 100644 module/Fmi/src/Fmi/Entity/User.php create mode 100644 module/Fmi/view/fmi/index/add.phtml create mode 100644 module/Fmi/view/fmi/index/change-rage.phtml create mode 100644 module/Fmi/view/fmi/index/change-waterdrop.phtml create mode 100644 module/Fmi/view/fmi/index/delete.phtml create mode 100644 module/Fmi/view/fmi/index/edit.phtml create mode 100644 module/Fmi/view/fmi/index/get-managers.phtml create mode 100644 module/Fmi/view/fmi/index/index.phtml create mode 100644 module/Fmi/view/fmi/index/manage-user.phtml create mode 100644 public/htaccess.appfog create mode 100644 public/htaccess.localdev create mode 100644 public/htaccess.zend.phpcloud create mode 100644 vendor/bin/classmap_generator.php create mode 100644 vendor/bin/classmap_generator.php.bat create mode 100644 vendor/bin/doctrine create mode 100644 vendor/bin/doctrine-module create mode 100644 vendor/bin/doctrine-module.bat create mode 100644 vendor/bin/doctrine.bat create mode 100644 vendor/bin/doctrine.php create mode 100644 vendor/bin/doctrine.php.bat create mode 100644 vendor/doctrine/annotations/.gitignore create mode 100644 vendor/doctrine/annotations/.travis.yml 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/phpunit.xml.dist create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/FileCacheReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Route.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Secure.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnum.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumInvalid.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteral.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteralInvalid.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAll.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationTargetPropertyMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithAttributes.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributes.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithTargetSyntaxError.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithVarType.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassDDC1660.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationEnum.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithTargetSyntaxError.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationWithVarType.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithClosure.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithFullyQualifiedUseStatements.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtMethod.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithInvalidAnnotationTargetAtProperty.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithRequire.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithValidAnnotationTarget.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Controller.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/EqualNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/IntefaceWithConstants.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageButIgnoredClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/InvalidAnnotationUsageClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleClassesInFile.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/MultipleImportsInUseStatement.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceAndClassCommentedOut.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespaceWithClosureDeclaration.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NamespacedSingleClassLOC1000.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NonNamespacedClass.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PerformanceTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Test.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/DoctrineTestCase.php create mode 100644 vendor/doctrine/annotations/tests/Doctrine/Tests/TestInit.php create mode 100644 vendor/doctrine/cache/.gitignore create mode 100644 vendor/doctrine/cache/.travis.yml create mode 100644 vendor/doctrine/cache/LICENSE create mode 100644 vendor/doctrine/cache/README.md create mode 100644 vendor/doctrine/cache/composer.json create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.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/CouchbaseCache.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/MemcacheCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.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/cache/phpunit.xml.dist create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/XcacheCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ZendDataCacheTest.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php create mode 100644 vendor/doctrine/cache/tests/Doctrine/Tests/TestInit.php create mode 100644 vendor/doctrine/collections/.gitignore create mode 100644 vendor/doctrine/collections/.travis.yml 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/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/collections/phpunit.xml.dist create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/DoctrineTestCase.php create mode 100644 vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php create mode 100644 vendor/doctrine/common/bin/travis-setup.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.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/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/Reflection/ClassFinderInterface.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest/ClassE.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/ObjectManagerDecoratorTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AbstractProxyFactoryTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AutoloaderTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/CallableTypeHintClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/LazyLoadableObject.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectClassMetadata.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicCloneClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicGetClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicIssetClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicSetClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicSleepClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicWakeupClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/SleepClass.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/DeeperNamespaceParent.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/Dummies/NoParent.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/FullyClassifiedParent.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/NoParent.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/RuntimePublicReflectionPropertyTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/SameNamespaceParent.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/StaticReflectionParserTest.php create mode 100644 vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/UseParent.php create mode 100644 vendor/doctrine/dbal/LICENSE create mode 100644 vendor/doctrine/dbal/README.md create mode 100644 vendor/doctrine/dbal/UPGRADE create mode 100644 vendor/doctrine/dbal/bin/doctrine-dbal create mode 100644 vendor/doctrine/dbal/bin/doctrine-dbal.php create mode 100644 vendor/doctrine/dbal/bin/doctrine.php create mode 100644 vendor/doctrine/dbal/composer.json create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php create mode 100644 vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php create mode 100644 vendor/doctrine/doctrine-module/.gitignore create mode 100644 vendor/doctrine/doctrine-module/.travis.yml create mode 100644 vendor/doctrine/doctrine-module/.travis/application.config.php create mode 100644 vendor/doctrine/doctrine-module/.travis/composer.json create mode 100644 vendor/doctrine/doctrine-module/LICENSE create mode 100644 vendor/doctrine/doctrine-module/Module.php create mode 100644 vendor/doctrine/doctrine-module/README.md create mode 100644 vendor/doctrine/doctrine-module/bin/doctrine-module create mode 100644 vendor/doctrine/doctrine-module/bin/doctrine-module.bat create mode 100644 vendor/doctrine/doctrine-module/bin/doctrine-module.php create mode 100644 vendor/doctrine/doctrine-module/composer.json create mode 100644 vendor/doctrine/doctrine-module/config/module.config.php create mode 100644 vendor/doctrine/doctrine-module/docs/authentication.md create mode 100644 vendor/doctrine/doctrine-module/docs/caching.md create mode 100644 vendor/doctrine/doctrine-module/docs/cli.md create mode 100644 vendor/doctrine/doctrine-module/docs/form-element.md create mode 100644 vendor/doctrine/doctrine-module/docs/hydrator.md create mode 100644 vendor/doctrine/doctrine-module/docs/index.md create mode 100644 vendor/doctrine/doctrine-module/docs/paginator.md create mode 100644 vendor/doctrine/doctrine-module/docs/validator.md create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Adapter/ObjectRepository.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Storage/ObjectRepository.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/DoctrineCacheStorage.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/ZendStorageCache.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectMultiCheckbox.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectRadio.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectSelect.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Module.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Authentication.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Cache.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Driver.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Options/EventManager.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Paginator/Adapter/Collection.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ObjectManagerAwareInterface.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ProvidesObjectManager.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/AbstractFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AdapterFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AuthenticationServiceFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/StorageFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CacheFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CliFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/DriverFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/EventManagerFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Service/ZendStorageCacheFactory.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AbstractCollectionStrategy.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByReference.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByValue.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByReference.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByValue.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/NoObjectExists.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/ObjectExists.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/UniqueObject.php create mode 100644 vendor/doctrine/doctrine-module/src/DoctrineModule/Version.php create mode 100644 vendor/doctrine/doctrine-module/tests/.gitignore create mode 100644 vendor/doctrine/doctrine-module/tests/Bootstrap.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/ObjectRepositoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/IdentityObject.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/PublicPropertiesIdentityObject.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Storage/ObjectRepositoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/DoctrineCacheStorageTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/ZendStorageCacheTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/ProxyTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/TestAsset/FormObject.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Paginator/Adapter/CollectionAdapterTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AdapterFactoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AuthenticationServiceFactoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/StorageFactoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/EventManagerFactoryTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/TestAsset/DummyEventSubscriber.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/ServiceManagerTestCase.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToManyEntity.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToOneEntity.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntity.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntityWithDateTime.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/NoObjectExistsTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/ObjectExistsTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/UniqueObjectTest.php create mode 100644 vendor/doctrine/doctrine-module/tests/TestConfiguration.php.dist create mode 100644 vendor/doctrine/doctrine-module/tests/phpunit.xml create mode 160000 vendor/doctrine/doctrine-mongo-odm-module create mode 100644 vendor/doctrine/doctrine-orm-module/.travis.yml create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/Version20120714005702.php create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/application.config.php create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/dummy-import.sql create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/migrations-execute-config.xml create mode 100644 vendor/doctrine/doctrine-orm-module/.travis/run-cli.sh create mode 100644 vendor/doctrine/doctrine-orm-module/LICENSE create mode 100644 vendor/doctrine/doctrine-orm-module/Module.php create mode 100644 vendor/doctrine/doctrine-orm-module/README.md create mode 100644 vendor/doctrine/doctrine-orm-module/UPGRADE.md create mode 100644 vendor/doctrine/doctrine-orm-module/composer.json create mode 100644 vendor/doctrine/doctrine-orm-module/config/controllers.config.php create mode 100644 vendor/doctrine/doctrine-orm-module/config/module.config.php create mode 100644 vendor/doctrine/doctrine-orm-module/config/services.config.php create mode 100644 vendor/doctrine/doctrine-orm-module/coverage-checker.php create mode 100644 vendor/doctrine/doctrine-orm-module/docs/EXTRAS_ORM.md create mode 100644 vendor/doctrine/doctrine-orm-module/docs/configuration.md create mode 100644 vendor/doctrine/doctrine-orm-module/docs/developer-tools.md create mode 100644 vendor/doctrine/doctrine-orm-module/docs/images/zend-developer-tools-multiple-entity-managers.png create mode 100644 vendor/doctrine/doctrine-orm-module/docs/images/zf2-zend-developer-tools-doctrine-module.png create mode 100644 vendor/doctrine/doctrine-orm-module/phpunit.xml create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/MappingCollector.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/SQLLoggerCollector.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/AnnotationBuilder.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/ElementAnnotationsListener.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/DoctrineEntity.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityMultiCheckbox.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityRadio.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntitySelect.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Module.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/Configuration.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConfiguration.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConnection.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityManager.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityResolver.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/SQLLoggerCollectorOptions.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Paginator/Adapter/DoctrinePaginator.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/ConfigurationFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConfigurationFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConnectionFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityManagerFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityResolverFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/SQLLoggerCollectorFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Stdlib/Hydrator/DoctrineEntity.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Version.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/MetadataGrapher.php create mode 100644 vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/YumlController.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/.gitignore create mode 100644 vendor/doctrine/doctrine-orm-module/tests/Bootstrap.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Category.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/City.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Country.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Date.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Product.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/ResolveTarget.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/TargetEntity.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/TargetInterface.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Test.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Fixture/TestFixture.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Address.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Session.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/User.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/UserGroup.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/CliTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/MappingCollectorTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/SQLLoggerCollectorTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Form/ElementAnnotationsListenerTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Framework/TestCase.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/ConfigurationOptionsTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/SQLLoggerCollectorOptionsTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Paginator/AdapterTestIgnore.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/ConfigurationFactoryTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/EntityResolverFactoryTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/SQLLoggerCollectorFactoryTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Util/ServiceManagerFactory.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/MetadataGrapherTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/YumlControllerTest.php create mode 100644 vendor/doctrine/doctrine-orm-module/tests/TestConfiguration.php.dist create mode 100644 vendor/doctrine/doctrine-orm-module/tests/testing.config.php create mode 100644 vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-mappings.phtml create mode 100644 vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-queries.phtml 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/inflector/phpunit.xml.dist create mode 100644 vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php create mode 100644 vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php create mode 100644 vendor/doctrine/inflector/tests/Doctrine/Tests/TestInit.php 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/mongodb-odm/.gitignore create mode 100644 vendor/doctrine/mongodb-odm/.travis.yml create mode 100644 vendor/doctrine/mongodb-odm/LICENSE create mode 100644 vendor/doctrine/mongodb-odm/README.markdown create mode 100644 vendor/doctrine/mongodb-odm/build.properties.dev create mode 100644 vendor/doctrine/mongodb-odm/build.xml create mode 100644 vendor/doctrine/mongodb-odm/composer.json create mode 100644 vendor/doctrine/mongodb-odm/doctrine-mongo-mapping.xsd create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Configuration.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Cursor.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentManager.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentNotFoundException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentRepository.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/EagerCursor.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LifecycleEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LoadClassMetadataEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnFlushEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PostFlushEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreFlushEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Events.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AbstractIdGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AlnumGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AutoGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/UuidGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Internal/CommitOrderCalculator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockMode.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LoggableCursor.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractDocument.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Bin.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinCustom.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinFunc.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinMD5.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinUUID.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Boolean.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ChangeTrackingPolicy.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Collection.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Date.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorField.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorMap.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorValue.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Distance.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DoctrineAnnotations.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbeddedDocument.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Float.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Hash.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Increment.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Index.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Indexes.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Inheritance.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/InheritanceType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Int.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Key.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Lock.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/MappedSuperclass.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/NotSaved.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ObjectId.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostLoad.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostPersist.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostRemove.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostUpdate.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreFlush.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreLoad.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PrePersist.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreRemove.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreUpdate.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Raw.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/String.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Timestamp.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Version.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/YamlDriver.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataCustomType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataFuncType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataMD5Type.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataUUIDType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BooleanType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CollectionType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CustomIdType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/DateType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FileType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FloatType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/HashType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IdType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IncrementType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IntType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/KeyType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/ObjectIdType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/RawType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/StringType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/TimestampType.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/Type.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/MongoDBException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/PersistentCollection.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/Proxy.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyException.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Builder.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Expr.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FieldExtractor.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Filter/BsonFilter.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FilterCollection.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Query.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/SchemaManager.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/ClearCache/MetadataCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateDocumentsCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateHydratorsCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateProxiesCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateRepositoriesCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/QueryCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/CreateCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/DropCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/UpdateCommand.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Helper/DocumentManagerHelper.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/MetadataFilter.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DisconnectedClassMetadataFactory.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentRepositoryGenerator.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/UnitOfWork.php create mode 100644 vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Version.php create mode 100644 vendor/doctrine/mongodb-odm/phpunit.xml.dist create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/BaseTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/CommitOrderCalculatorTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleCallbacksTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionsTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CursorHydrationTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomFieldNameTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomIdTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomTypeTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DetachedDocumentTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EagerCursorTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EcommerceTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedReferenceTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FindAndModifyTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushOptionsTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/GeoSpatialTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/InheritanceTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MapReduceTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MappedSuperclassTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedDocumentsTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistingTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrePersistTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrimeTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceDiscriminatorsTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceEmbeddedDocumentsTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceRepositoryMethodTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RemoveTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RepositoriesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RequireIndexesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SlaveOkayTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH232Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH267Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH385Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH389Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH426Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH435Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH467Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM117Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM140Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM160Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM29Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test1.txt create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test2.txt create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM43Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM45Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM46Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM47Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM48Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50/test.txt create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM52Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM56Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM62Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM65Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM66Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM67Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM70Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM72Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM76Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM81Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM88Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM92Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM95Test.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/file.txt create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AnnotationDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataFactoryTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/AbstractDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/YamlDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/EmbeddedDocument.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/User.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.EmbeddedDocument.dcm.xml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.User.dcm.xml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.EmbeddedDocument.dcm.yml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.User.dcm.yml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/YamlMappingDriverTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.xml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.AlternateUser.dcm.yml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.yml create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/ClassMetadataMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/ConnectionMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/DocumentManagerMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/DocumentPersisterMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/MetadataDriverMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/PreUpdateListenerMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/UnitOfWorkMock.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/MongoCollectionTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollectionTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DereferenceTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/ExprTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FieldExtractorTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/BsonFilterTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/Filter.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FilterCollectionTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Tools/DocumentGeneratorTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Account.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Address.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Agent.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Album.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Article.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Bars/Bar.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Bars/Location.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/BaseCategory.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/BaseDocument.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/BaseEmployee.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/BlogPost.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/BrowseNode.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Cart.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Category.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsAddress.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsArticle.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsComment.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsContent.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsGroup.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsPage.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsPhonenumber.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsProduct.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CmsUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Comment.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CommentRepository.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Document.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Repository.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/CustomUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Customer.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Developer.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/ConfigurableProduct.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Currency.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Money.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Option.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/StockItem.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Employee.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Event.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Feature.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/File.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/ForumAvatar.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/ForumUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/FriendUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/AlsoLoad.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Embedded.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel0.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel0b.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel1.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel2.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/FavoritesUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotAnnotatedDocument.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotSaved.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotSavedEmbedded.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/NullFieldValues.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestProduct.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSellable.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSeller.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Reference.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/SameCollection1.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/SameCollection2.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/SimpleEmbedAndReference.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayCollectionLevel0.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayCollectionLevel1.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayLevel0.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayLevel1.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedOneLevel0.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedOneLevel1.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/MODM160Level2.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/VirtualHost.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Functional/VirtualHostDirective.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Group.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/GuestServer.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Issue.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Manager.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Message.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/OtherSubProject.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Phonenumber.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Product.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Profile.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Project.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Server.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/SimpleReferenceUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Song.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/SpecialUser.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Strategy.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/SubCategory.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/SubProject.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Tag.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/Task.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/User.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/UserUpsert.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/UserUpsertChild.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Documents/UserUpsertIdStrategyNone.php create mode 100644 vendor/doctrine/mongodb-odm/tests/NativePhpunitTask.php create mode 100644 vendor/doctrine/mongodb-odm/tests/Stubs/DocumentManager.php create mode 100644 vendor/doctrine/mongodb-odm/tests/bootstrap.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Account.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Address.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Phonenumber.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/Documents/User.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/Documents/UserRepository.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/cli-config.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/config.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/index.php create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/mongodb create mode 100644 vendor/doctrine/mongodb-odm/tools/sandbox/mongodb.php create mode 100644 vendor/doctrine/mongodb/.gitignore create mode 100644 vendor/doctrine/mongodb/.travis.yml create mode 100644 vendor/doctrine/mongodb/LICENSE create mode 100644 vendor/doctrine/mongodb/README.markdown create mode 100644 vendor/doctrine/mongodb/composer.json create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/ArrayIterator.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Collection.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Configuration.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Connection.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Database.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/EagerCursor.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/CreateCollectionEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/DistinctEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/EventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/GroupEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/MapReduceEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/NearEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/UpdateEventArgs.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Events.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFS.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFSFile.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Iterator.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/IteratorAggregate.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Loggable.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCollection.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCursor.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableDatabase.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Builder.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Expr.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Query.php create mode 100644 vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Util/ReadPreference.php create mode 100644 vendor/doctrine/mongodb/phpunit.xml.dist create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/BaseTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CollectionTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/ConnectionTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Constraint/ArrayHasValueUnderKey.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorFunctionalTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/DatabaseTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EagerCursorTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EventTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/FunctionalTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSFileTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/LoggableCursorTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/QueryTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/RetryTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Util/ReadPreferenceTest.php create mode 100644 vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/file.txt create mode 100644 vendor/doctrine/mongodb/tests/bootstrap.php create mode 100644 vendor/doctrine/orm/LICENSE create mode 100644 vendor/doctrine/orm/README.markdown create mode 100644 vendor/doctrine/orm/UPGRADE.md create mode 100644 vendor/doctrine/orm/bin/doctrine create mode 100644 vendor/doctrine/orm/bin/doctrine-pear.php create mode 100644 vendor/doctrine/orm/bin/doctrine.bat create mode 100644 vendor/doctrine/orm/bin/doctrine.php create mode 100644 vendor/doctrine/orm/composer.json create mode 100644 vendor/doctrine/orm/docs/README.md create mode 100644 vendor/doctrine/orm/docs/bin/generate-docs.sh create mode 100644 vendor/doctrine/orm/docs/bin/install-dependencies.sh create mode 100644 vendor/doctrine/orm/docs/en/Makefile create mode 100644 vendor/doctrine/orm/docs/en/_exts/configurationblock.py create mode 100644 vendor/doctrine/orm/docs/en/conf.py create mode 100644 vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/decorator-pattern.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/implementing-the-notify-changetracking-policy.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/resolve-target-entity-listener.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst create mode 100644 vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst create mode 100644 vendor/doctrine/orm/docs/en/index.rst create mode 100644 vendor/doctrine/orm/docs/en/make.bat create mode 100644 vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/annotations-reference.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/architecture.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/association-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/basic-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/batch-processing.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/best-practices.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/caching.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/configuration.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/events.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/faq.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/filters.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/improving-performance.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/installation.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/namingstrategy.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/native-sql.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/partial-objects.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/php-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/query-builder.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/tools.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/unitofwork.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/working-with-associations.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/working-with-objects.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/xml-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst create mode 100644 vendor/doctrine/orm/docs/en/toc.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/getting-started.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/pagination.rst create mode 100644 vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst create mode 100644 vendor/doctrine/orm/doctrine-mapping.xsd create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Events.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php create mode 100644 vendor/doctrine/orm/lib/Doctrine/ORM/Version.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/.gitignore create mode 100644 vendor/symfony/console/Symfony/Component/Console/Application.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/CHANGELOG.md create mode 100644 vendor/symfony/console/Symfony/Component/Console/Command/Command.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Command/ListCommand.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/Helper.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/Input.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/InputArgument.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/InputInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/InputOption.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/LICENSE create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/NullOutput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/Output.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/README.md create mode 100644 vendor/symfony/console/Symfony/Component/Console/Resources/bin/hiddeninput.exe create mode 100644 vendor/symfony/console/Symfony/Component/Console/Shell.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Command/CommandTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Command/HelpCommandTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Command/ListCommandTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/FooCommand.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/TestCommand.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run1.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run2.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run3.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run4.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_astext.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArgvInputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArrayInputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputArgumentTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputOptionTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Output/NullOutputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Output/OutputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Output/StreamOutputTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php create mode 100644 vendor/symfony/console/Symfony/Component/Console/composer.json create mode 100644 vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist diff --git a/composer.json b/composer.json index b70734ed..bac02754 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "zf2" ], "homepage": "http://framework.zend.com/", + "minimum-stability": "alpha", "repositories": [ { "type": "composer", @@ -17,6 +18,8 @@ "php": ">=5.3.3", "zendframework/zendframework": "2.*", "zendframework/zendservice-recaptcha": "2.*", - "doctrine/common": ">=2.1" + "doctrine/common": ">=2.1", + "doctrine/doctrine-orm-module": "0.*", + "doctrine/doctrine-mongo-odm-module": "dev-master" } } diff --git a/composer.phar b/composer.phar index 48edaa6149f3581df05a06f283f27ad50ff879de..b3747335c25cd9edd775c0662cff428f000a368d 100644 GIT binary patch delta 204556 zcmeFad3;;N)i>TNOO_*Va=bgSbL}XR6x;HW-LaiGjWe6kE1MY{`-2BqlK? z^l4ejk}$wPX(4GTU4W9{LZAF4+U z@lwiI_s(+W%$c*#+@pUiyZ!o_!{=!!b8BbU*3PV*&3d%u$v1BO+~=lf>|U+b{lLv=xAVQ|`Q>_BoM zTa=XISU(Wh&=K~I;qEU+In$rn`q_v$~)VC#}g*=0$Y_BG}51(g{;zw*cI9nm6% zJ)M+g|AqpD%G+yRtukJgVi~&p-ElKJ=#<@1dGPkCPMR4@HBYpEAx*xZ^0(J~`H+Dg zw#J-oUzQ?YQu(Wl1tGMWX%9}252@UE)DtoWWj{A(+3!*$+Zut zmQ>#Eul$w(qRleV9>|nkQu)jWzn;tvs#XUr`SxQ9p;VqY?wuxH9=A+QBl^1;L~`B5 zS4@urQJtJ+KhG+=qcUsd$wNS9wS8WVd`RUR?{0YvL^InyOASnA+E4DjQGok)aE;i4jTGTFwSw`SY_ zs1}LJU)=xvR%XvFWN#;@vVxR+`;XKkwv7*7$_`DKZO^gE4yfFJWk2md2ea9-3ZJNy zFR0vj#zQw5TX+sD%`C3i>FxD*uJ`o^!~RGxG?*|>K+ToPe<=3l;q#%=Qtv;1+mA}};^ob5upE-eFT4%~+dWPBlb+r~$R{j0LMqrfiWSh3Z*D(N$oO?60ZaLFL!0esn$2_`$3+_P2~&yERpUN#);ecw(<;U7tBk z%$)$(Y`d;q5GWBtwLO{f%q;tpipx;BrtLd9?2z5Tew*nKtu8^Ug0Ii-r(tBHJbTeR zISiG@|GVBSdOVnAXT$b93FwXXR{6)9_9dbll5BMwXFM&TwX>@12`reECMykREs=jb znYCK}Q800p{G(^0dUbf>O8Mj?@mLe%e{a|-43QgW9G zgxw9i-!}CAv(b_F=H}VIkuPDT^1w%*t7gZt#K`Oixw^gDEniakWcxKQNE$`oIeC-q zw<@Zk@|uTdo&!Ch92-lvpfc_Ai+(Ig9xc9-mt*f%+nCDpzIxWfpPCyX zb&J9Vm5!8qetJ!Qo&8G=*)f$r{>cAs6qNIFGTALZo{&Zj52>|dSLZj`zo&js`Nobj zo>QY`=TOanVoFq=Xlh<7I0~@lQOZQ9%J^eNAmR&Q;eFIBU-e1lmmhesIJ!=(}o}KQgA(E;SgHtGYM+_t;6v=EP_*A76N>KLDa6I9Dk& zQh8#U|8c?ifyVOafa;Ic-T(+!w0T4+8C355etWU8k`>W5%e%+GXji+3%0>5I=}~l* zrZ@Haz2P|8S*#$U@-Mz+*Fp8GuwSJZF_q^$bQ^X6bTrx{Lj`tw^F&EmRKB|EqTiFI zEM7iTkYZn_#-;L-WAn`hZrCk_nNwT(`Xhs+P)9;ue;`5#c|)x-m7n{|q=(|`v*f~T zd$)p*%H8i4%wz7t7W+@sx>5P-?+rGy3kwx%dZzG9xrv=cGwkz}WT5g-RX$+5s9C=K zmhs2iMQh}frb#XKiNAIFO~%RlN$)GJofA)mZo za+Y99F91)!dUg71LVTj%g4STLm%!4e%VnYR8}H^vE3jv$q}U%)+8>p}mIr*0k%f$) zx>L8ZLOtESR;dzHw$~qQh=RRMS6~n7XGln%(pL&d{8+M{x4&E=7^oe>xV6-5zkjxb zg38+-J8%tnXJwIZ$AB*^misb=9V%;z?p}e{6_Z*+LF|-1n#7&Tz@pN5V&7r*oIaa5 z%hJVwC$QR=h5pY@T9#^Q5RiEWyI@0bAk^VY;5=6G8`8)n zN&-{)aOcr&Y*CrRevU)VhRR?6>f8)r7ditgbWGa7t#)nT}Th-X_R6B;s$}j!muTcDJ?deKZP)%+|*>Y zermqmuh>16gXf0+hHgrx%K2<2jnoTel!oh3^{8xl!&Acsr`F0Pylbl32Y;VhX+K*j zqg1}|qmozIM^g*gDo2|ARVAybESh)na=`^YbWC8EJ2LGLl*l<#>HSveRQ7Gh4Ex)P zZ&3MzmG8bHHqeI-haA;0t=xW3mTX4lwuZa}RLI140^qPv^c zl~|%LUUcTzJxa<`dE}3lJW|2hU0JN>-83Ox&rsWw%6WgfaGSteyGyEnPhj%PH?TwM zVJ>^nm1@`2+EV#q-#--uJs9GNB>s>n#=^uzJ4Tk!> z5hZG$Q>dWwrKP`oL%?3+F0n6|D_4uk!X*bA#f#l|@oP0al{?-VS_wx;5qrd)X1@m> z6;kD>9Nv6MhoG6=?u;U-m=RfKDcYv;4`o$$Ry@OD_bauT%0KY+MQj}&W;M#rs9f;u z1J?=9%FQ#Bf_vYL4GAX9ymNkblzljF5_`M6jGa}UV;{i+5-e0sSozj1Y;XB;`_K~k zkjiy|c@MFlm9H&)Q*C}Kzi|BG{l*4vVlS01DBBeA_X^IxOpzm%8|Dq&X;fQ>YO`2> zg`)EtD!OxN+#A5Ezc%T`I-}ZZRn1nmD%kDsfXt4WrPrb^wV_fU`Q1D0mIZPm{S`L0 zzOu;vrW%{dy>I>TJM5;KScRI)3j`f~OZ7tKbB~m_v*VRiX)61Er83}NtlXMuQ~R3A z`{&lMH)l4oVo!>2_OvSzNaf>im3{|3w6NDbnf5bPBP!qiuLrJRDOG3NyA|P3Ik$4z z57~~YO@(hLv{Sj^;ER36^v_~hRkK*22DVbQrEo(8x;c?Uit2;P!ITwFX0M;b4$mu7 zHPhu@KT;i$f4FP5+5e@8h04Y=pZW;6=$3DjYd6~8JVTZr*|C|H z$xWf|fj*zqJ#JPbQ+Y<)U;OOZ+Ew;hin6J^{;HS0%id|uX1C0&W_>d=?N_T7R6gDo z=}|8i+P|Y9qVm;uKdNDG&1|(lq!cA8t=7B?ft@HI?D|<*66D)won82z5)4!xFDsaD zplqc;nSz~dotxrx#@x?cB_S>@`t9v zTNTVRP{GPVb0$hCE}e6}U|+f5qZwEJAXy*{HS`UUkk8vEnFcM_VYRwcdb+H|%rlS1 zDzZ;iS}~RXXVvG<7PF->(&uH_7bwJ0*}8OMg|W!BOMGGB-wcMTVt*zG@;enxQ+Z?Z zB|cVD(@+_!TeZ=*I}-azAo}wp0Z@6*!Ka=AsWw!^s#KlT*Bkpq4Q46!OyzY?Y$;^> z=QPyBs#LA^hPHPGcLidPsMS}Lc1h(m?)<;9Ee(Qj8)Ma~n)>^D{T*I%J{eDyAL>1| zEL6HzymB&O$=ycf1hmkg#n6F>Pf%$KeDlO;Y>;XbYfLR>DDh6^3z;o>Sgc7CWJta)?J;WGHor$}g^H_ychQ@|{x%LsSxS>~*!Js9gV>pZVC7x)%GNr^xwH z*>rH>>nx*o60=vWlj9r>G>V60*Lgm;sebzR+Yd z5Zuh3UsUU8+1*d;ft^sb-q-EhO)bt*PGu?=?YsLzKrV)#8drH)Z&2vbht%GoQr9;> z41y7LYvbz1{-oabDE>pG^Qi~y20W_>p2}F=Dk#tciH5`ig;FY0-}>DPpkpyuMN_A@ z9|}~JDifGiSk0fx&~@jxffLg&}*o1ZkAtrjCxH>8!pPZsG3ufReXX3>?Z6Dt4p z*VoozL$=$y)h?y7^k@8aHe7$cl#FlIZaPA(rWY{PmvBBgT@m8y8KL^Lu2`*32u^ z{HV;^^%1+JVlwdn=(f6jkK!6sUiI$%_X0%=mPDCW)mlgkA?VfwBdx>*Bfie)TO#nw zReMoRoEN2rjkH#c(W(Y5E%o|)2SSOy+dMTzD*LWzuP~-~Hg!=K>q77$#j(cg$D#V6 zviaKZ3*cZY3qMc~sXX~s&l5)Hvsm@w8SMThrLR5Pw8DO0h2)h~Zhl~EI&&_rFML>O zVN~9p`u($w_6tP&+ZN~8OB9h)dD-3ecd*5?*-ML;idEi?P26+zS3B6rmgNN-2m6Kp z^k<4zsr#9_MWz1Nx1M2j%Vyf2P{339y`!%@$%dBI*gI8CD$hA| z+j92MvP%0iiioJ>SM)y3K3G;h?_H%=Q~CW{8dn(DfmZUlZT>)2hw=l*JDR6l|AjNz zxy$p#ltu=ZYnsK^(G%3!g)7dsZqR!ok^V6I?TXfv1vcMqf5cTYQn7M_mI~>ubgnRe z`jo{xQkPYcH{Q*vDt|@;OZ=dz~*pn$Hs7wt?<$ zf1ukHt?YB#_ShVe9)Gy9(dfXZH^lYU^rWefANN?y{Gzk1CG6m;EBIAkH(B||uO=tw zR5o_|BAb1oZ4@aSbh-7$2AyA&V=d!{&Pyt?;mx3Y%lCcVlq1>{j@M@90^O!F?d+l? zK*+PUCS{+x?kuXy-?>Yh%*xv)@!C=?KP?KnTi?SUc*3ezI{F}p;b{(Uscyr3!a!3X z(&e%_w@mZYbgf=oHO*7o6>ig~g`E{P{*}uurE_e3{*F*E;_vghBB24FTd&kdW2V9u z->=QRz&13pe|487*JcAyj<9goVYsefzc1iod)7~CsL-7uCn`FyJU~|Tr6bbU-?H0> z*SpH;J!Bc|k@k*Y|6qG0*e-u^?_qO}v#X;QkpwK7=pJ>A2eL~%7zl)Y5vrU!lDgra zT4rTP?;Gs*hQqspp-$%lt{u{Hm}Aqc$&!TlD>o+>Oz;jwdc<#5zImEO{?*i;$Ijne zq<&?dSl4<=?4maFNK4y(Eltj!%$Bf=mj<*+tZj=m+FLbyZgcF{+~{)}rG(wp?qXdl zr?QK>tbEZc$r=2H3Dy#>f8J8akNwm-fj{$zRcD7^PUa`?wwCkYaceUB=Sq87M=#bv zH?m@MSarTYXINLt>x#9uJ-q2oQ(2xr0QVKh!|ANh9XpLZ#gG2fIUi$mwJX@EyH|)bt{C??pa!pxim(v~Jo8HjSpa%wed-a9N0OpT=(Ne${ zJZ4QVtI$h*6v%Knx(p{8HIDsQz)PJe`I%x&ya)=1TLpi9nKjqsc5|;IsgR%d$K+H? zSJ>y<&fok)@+5nOUM=8}mGuVR{%UeMi7p41@weKDM+CyIK7Xq4&Oojy+{t17)ZbMSyz&zh#$J z$6D{sV9yOt;5R;D$>lRIwtSxJ6Rc;j2M^g&jBF>ie`xiD&_JM4_y*XCp*$;I>DY$s z>Ti^49=7j$mC4d?z$zXuo+wkK^nka|r$>T1T5_$$n#X!iOt)|FMZ(ll@AU1|`+}X< zw^;L&0>MgFdj52F*{kU(>bZ|;cTFvc4jQSi@&~$tjXLBK**(;0Sd3oBwmwl*7^|ow zd>N@6=!e_gSJ~0W-g$G|1PL3k&K6ykyG;Hf=0&J3^#|I$p^z8!BVSbL@~2HsLz-!I zb*<}L+E=XEu(4^?s`lp9OTc{(ykIHOE5@m|sdY)y#uim;#|xI?oC=%UP0K|CcKW*f z0bi$!U;Tz}i{E_9BRKCz;$>DcCtEF?h$zn;33#;%0(=6Ha z)FEE6byIU#()%01=L!V%8tHfJz>tG?&eA5PNewm(I;dX2cU+N@cg^2QQaV+Z9;?1M zx7uhUNmQmQ5efS{i34{H3dCYZ`$C~$DC}`wb5A}ixhr|O(aC5k#isArE|(+Piu!|f z-j5xi^cuZiM%=<4_Q+4pNmEDvG3&q{_x!NR$D=G}8!RtU3rN zLOf50NR8k@xK5YyTPR7AkFEEG2YSJN6$>=<1Oa8MYCp~}!|~&gmIpKh0zNReolu`X z<2cLF-Wa1Fyg;x*sX`!AVRxh*lWh-*{IIZVgU~M6nNu_PBR?>w4?Ah}x~eMQZf{?I zug?<WU8^_b z?fYbSHpm%&7J%w8|I^VBVIwTiF%Crh`7~1wOaD`Ljv?-8)f~#u;OkzqW)H{Fvw9x# zoDUc3Ct@ZMF$S~RfHms#3RHlZ#Sa$}q8Mb1Xf;SsLBVIQvt-)9`E(x?5`7OldxvA# zXc;68QP!naf(^Qy-Qc+c+bYFNkRo_I7S`wO=)u%v3vXC=bO!s7VWBrPf_Q@4eE~7L z+=XMQ(dkk3Jx)F79i~ye9UZU>J|h+o=muzO32c|@k zZzw=q4(buqP9w}-*{J^Xxt7Akh9uCsx;WuQX#$e>2wIR_oJ5hMe_$JW?`aWyT|6-c z;CeMwmtQ@yCQacA13I0&^jAm`ZG3d5S@%G#*z;)BG~J`OE^lpbS-XlvCvcmni%CTD ztZS0;`G0?CvKK}9hf5fUkQeL-a?5 zzTF4)a7_Qv?x}xZNQ{fF3;{m;Z%!YKfPjLo=Fgov08k$pOX6ogHrBl*QAkWB`y3p!VL;YZ3%(-vT7eCiGo z+gI@R7tHySouh=Yleo2%RBkZ@A;CKA+$4Nbq{P8K_Cb&nrr<^z5YE;Lz0W%cbv$9V zIxtP~O5KGO^@$y^TDo~$ZjaL~Z8yV`i(4)qEJZZ+8u}p={9({)+!6u+uxHv3gNEKG z;3*@GroDCj+Oy7XU*EEBQ_F^p?MpYUTD58YiZY$dCsAR;scQHFJN=1$%i;Y^Ye7UGf?GfwZS8oj=jiQh1)#aBU-s+}|AYr=V>cJa83Y&Ha7Ob?4O zFgE}piAHjMQ^2>oAC?vNg3wODe~;WZi9fg7WZ|1une^f5({;r{nuDECW`uWWR&`DM z?y6V1OXEhcCaHu9txs&PxG5;P;Frny6~c#57FGO|a$H&2jWDmbSFfyu%cTXbU`dA( z=kZ7@qh$~I`hq*bw&1Waph)0v4j;55{^+?{-elV1aDkF?Ilc-s!&snr703ZQ71^uU z+S11*MFzX&*N*gzgxsTbFZO%z}Z1;3x74-wAv#q>Bp9tDuug zb6Ow{3&b+zMo5*qpx{yb#LunSy!BPJS+vBXK zJsN$uAlWvAU1{U`>##k4bZ_1;{7`<1*J4b>K-=-Fy_e3YbhdX1;{XtmBdgatxFX%r@+&2 zoSG@Yq>*UB(Yy~k!4D6QPH`l2qGf^;pc z(`VFIMLHYnBb~Zn1r238%NqCUGoTL7K#XGsVjK(WM3ub`_mI$>6ev@%Z$-}%VN!rl z=U`&uIU*eF&@XcMD|wowF8UfcuB?=ps0{nNaTvO{a|}A7FMto8m1mkHIvGyvgdE_N zI6~OFE952Dd3a1W2`}SOt7yjvB}rl!<t8W0(ZlJ$A1TIAz zi5krI%=Fai!O*bYXBh|(G{c(Ddb;Ja0kQ*bx0TRj*wsn#Qx5!8@Cf3|BMEqL0?Oe zs&Uz{+~;bhLpH}SqzC*&z$=JIAf3cr%&$ex8HMiHeph~CV?U2tWaF_|X7xij?#5f^ zVO1fv)W)_w6}}TT{bxn@7;F>%KP$QuApo|!x;_=U6=6j;BF%7k6h#wl?!x{cB@*(X zQ--9*>~4Kmk01Ml_+(tJea;ozwWkX#?6+#?zz(lI>&Uxa>8lGH+fXG3l6{Q2|8Y%sw|h7CS5 z&T^j$7h{-|!iEsi3Fr{mLoW8s8m5$*{N z^uiL7E*DINVkX1U)yMi61SXKGKp!(D)N_tAol~;k9RmohNd_$Wu3~PI^A6AS%pyP} z=OuOxCJE_@edy~Pe_qC{b>y`m94OXRu<&wWlJy~Ct@rza(03~IZGx1^_0tE|E@HdB z&TbzzirOc`(1&7E1QU$sfr=kVE~sSMSWYTbSJ4ctS?RA}pb3o)Q?@j!)Q~Y%>hRg6 zk34F#IwAZhLo68RRQu*Y+C*|^q_awLRgZCu6k!zC5 z_g`#jy;P$ZUJ^snT}ARpLR;&K$C9qAZS1jJ7MU=7!gCTG@2 zSDO%xMrE>bd|n}ih#VE?CO60VkY}vAC!gKb<0uj8xjd4tt0)HvSk((Va7S*M%pxH* zu*=B~ex%nf6*(pn97NrP$wuU%8rA7g&xktUnxa}TP3j}|0#%(V|B3Y z4Mv;MzmK2tDtlZKY?C|!oMaQn35FLR1_Sh_o=$!4lbTKm@)2Cyxy1cLq z#T^z((d)-~Yg%uln9pM@adWM|{cLzY0O#$$AhmrSp$B-$7 zw|Y&<{Jq1PLnXuMy!?)o+>xU5Hk$eUE>kXLBeCJ{WU>c3ZTw(S(gb!}r%r#A^Zh@t zOk=nFvq%U#5_|Lc@u?OYU)*DvLb8v&*?9@Kdn^<9W#2I8u%~=KW359I`N@(LYcd_? z@P$~`NCEq9S9V^r;2>L9BDf*{6sPFfk*?h9ud$@@FD08M^0nQjiR__~bQ%tDKV~W( znb7^QmKoKtNNR+Iq*}5XQlM|3uQK{hT3Qelu7Log0tJ=~_6Q{=%CkW<)QP;8=*WpX zSmqs8GkjtZ_Rg(S*}^+Y3+adM!>KH>a|K}Pvw^*}nNbQ2irL$ga z-8UR1OM=3W1igbYMkydho;MOcq6*qtxBGnkl{-7AI-9XQJG&D*MWj&f>0obAFm-ZB@+A3qnZy+?ecA>pYeI(ll zaY8uZNE)wihT`Xoc#(V9+u!4*K2YE78F{w%V~w4&W2$E3@3d?A!*Fr?unU*a3Pin< zQMUv3Z7efI)`V7$NI0g_+bf+R(mfz^vPmnJ)da1Pqb9+^mUU^lRn57vvB#+hDZY9o4YZS?L!FSbs3nOsN&@(J$nHZ=oy5e{?OqMWf)LN^F8ZJ&V{^!ldaq>|;oz6>|J)QsgkdNc z`57@9hBL`V$-;Sj(H@JHx%!KeIjmqE3Vl;f=6C)>5WbmGX-Rb!X*3$=p= z*$kpqi-S9X*}d#U7X~uBH2R+ACwVAApvIyVJ>}-E&j3YT;ELYT>ix@&B-53 zH{1Am>1GSJzLV^zcd84RkmX?XgKbA?1KKO(C z`IPr9Mf|(T=42o>Dtj?xx7A$5mwz)UW0uhV2&D-5i98R&5z&A+LjnCWx)xg;$X210 zmmzlNz$PugY&#o@%cRj6TmIP0JY{Uf&iPtk9qQ%JBy@1sZHuB%xVxG>Q`?fhZAKO6~ zVk;zH@`Y%R`}^4CgJ+mYkK+eUBo!_Y36=i7%0>uyB&n#%Ve(^BNCZ=%l!2w8V4vJa zPQ}v@5#?ugXw#YZf`#n13w)ZNS@%4cH$L?2!97Gz1LUSSrX$qD|y&lR(?tyg_$SMni?}3GWPeJZ^X>!ep{6G_sPn)EoNR*%g79 z@;2OtIIBvkvbk-7v5-OqQ783ksi+X*9r8Jny>V4WkE>dweM^HCb|qzj`vWe796EM& z0)hC23s6HofaVgmZ}iEMU}Py-rs8orr8dEMkXe)b@z<^A)C%t_gg#Mx96%rj+5!&a zjo865+RtzPhgMh%gOYeEtZO0C=}3}dI}&n?q&AH4n>T(ZWs14f(G}+28?@{s^uV7F zC71D5#9A{Hrokfd+|BN|uXIVAZeA~y$tIN$t_M4Ws{s})zFfhl_qMJuD~sPI}xDNMy1;lZ(yI}`g!z5*`Tnzt)TqVk&d%P5r*2bM0hXvHS* zkF%dK%`?-e{KSVzx$NPmY?<_HixdCt33fW$hIsHuQZYX}oHVgTq!Kt`oOF2%7Aa6K zrJNiQ6x!Lj${*McuRG2}LTn*|xm_@)jpCpf|9FyCYNd6-|M-bsb2fkRNKzI*{*$C+ zwrB4Yu0Lh5v)}%%h<$Hw74er${^Sf)67Rk5=4K#5tacqujd|bAXG_j`%(z4=hZ|;a?uX(E&qS&S%pedzPK| zO1e-KGidS1cy?~CxZ{-hKu5~m@)jH3ZlSmnhpnSvo+Q>10H*2i_`{`|d}-kk`y zQ$7?a$zCTr`Sr={uFqR0M4z(|gW7JH}QZ@>`rVwcFAZxsB zGF$v#_wo}j>lAODyb;FQ&4* zFHX#e4lA@M_UNUhW+E3duqVhZM-+47(lVC)_H@>FERzi#Yb9HHLe!RK)x40sd~AN) z6!M;H)P~sYU!2WMe<~XZJ^KwU;pM%o?y`Ee=H|2%xwqU4Mg}g+(u!2pw9T-nX(!4W z?7+>%d`GXT5JWlhUzcBON|kOBkzvD~?$-8S~}mF>Lj{iOV9zEHHvAoVGrsBFXzhI|ycnqZau zq7Rbt3ycTE=kQRlJ)#Wu+*NrQ^asT#9rVWnUapzv^Zvt{g&n&(k6m?DZl;hMh+nq{ zw+YAQ&{fl!|EhzTV)r3D6GH3*H%35XF18RL;>3+TIwR;v*a z$G^x>Kt59S)!&~W5KrCnckZ|5H4`hKMX_xU<7h+7N3umQ32~}T%wd%;&@ECg3<`-p zsOBf{!&y(yew+?w=GoCHMf;M}K!}L8>GSS(#dTZ3*WF>sWzW8lRxMNq#lsS-Cp;9V zta@?Ij&@!iZ*yz;%cyD0=WyEbqK+0Z^&uOjr@I`x{GE{=H(S|JR3zD=yn&>52@)9s z;?hELQR$1Vl_j!nWhG(G<`7;~r6h_~+ZW8mK@KjMa6IC_{moR!4jwX0_2*d`c)MvVp{p0j_R8G!#vr6cVh$cp;iPdA6HB1u0GS|J8&Flw~ zQ;glq7Gb;D57x6 zN{xe&?=RB|^NFT*i+H`VGYLu@(q9xQSTmm8Bu5Qz5h&YDtP1K5&d;fQBB_1R+@%yz zGb6sqzJH~gwK7YAFAin{-!yaf@U_ibjibND0K`)jWP&}cfTAqWWaxRL(13xAE`%tN zo&=cf=!gIMTr+xu*V{O=N(Z0Sj3mQUz43*2CrwQ?+QP;hv8Zq^fpouaT@Kni0B)G+fbv zpd5utpq}hi^r>u}8Da-xJ(O@ZAi(a(Fnr{K_-*W^uTDyf(g#Lj>)tA4uiiX^FV8pS zk4(8`Nm3ylox)`TLJLRgn-P9KKRn|7+CJ?w`Zn2ZcRasX`4i~WYScHVyJUR(YF`ut3k@afBH{ zN3YpSx4!k+{j}`r@1~{f-MJJ0?v#q{ecxTfp1C!PHNKgWRY`t8kyJ^ZE@zNx6qn;f zXP^H9#2TV6^-i351Vjl>`ud&jk*3>hCSHEfY-4lZ&B-0tzd&LuJ;6E0^gpfOe}%(s za-6#jWTi|gntV1awq)n`bIy4Yc%aY=^M?A_l;tsuq(e6=E09FY4CMer?;NY ziL=DFb2Vj^3#C91X!ExlC$3dX*A!hjcHq67Y|8MY6^&bNw)@+2*jL|6DI}tUcDYcg z|4#M0jt<~YW)FY+C_DG=eMTa{V*Xr%DU0jfru0(Oc|y)f1L!2+@)Mn>-5;DEJ3{rih5N(E(3-C5_kx-20fhfZ2~vF+8}{7vm25 z2*tfnN#(Ax^Zr(mCUf73jJpM)JLv{Nc?_H)-`INOgi#stsyW5nMe=~JlfFYijeTMd z*kG~h@*%yq2u}oU@kyKLA{5yb96|xaB*Yo+N^nN1DO5}9r&sPa7@T=>umO3;lhGh7 zY7+odTc#|mT_N*$DLO#_C{KL>e%x-y%vWBG(k5RHOrjg}V#D61H@a6DAIK zL>agN0HrA!aT$uZfY5MMApu^=DKV)rD<$SfuZMjy_9I3+bu}MtTl^DsqfJvld*}dn zJ1K_Ka7;o{L(IP>!b)(P0Oe#;%8!#IbTu4^G>+F-v^p$J57H!b_`SqQJ<3+|hUgSa z{4mjg*r>6_CDZ8SeIhHJZrvefEHgc9GJqYMi@bb8CUuX(6;T^C69HWjtL{}qo-oH# zOcA+(ojBDN48>1mW0Xqa^$e-r1W8cX%-AYrElXBxT)Vyu`C*7as$*&rIJh8zZ8FG6 z)+KHgm3*#JS_t~O;ZL=$)LlH3QFQn2@(zZf3qbygI64kmVi!?lPh4Z*(VHj*hr-=- zX+Z@!^y0m_zD^=?qTU2P5?dh5=~IxMWGv&+U(4E!@ayuL`;#Z}MR#ba{KChQYx$GE zNzPZCOF9A)x4BbjYb0hZf}aSd=xk*0&~np8HHbsRVAU-oAV^#u=0p0n&}r2%D7Ixs zSvKZ?tES{qETukhY#N;#9M)}HsJr+>f7kR(!GtC6aoZp=P%B@6fKK7m5cgTAet7lu z=A5;$PEjMF!s*?l2~`Ou8UVIJ))(`K72}s4k}(x48-?E;9vf0+aZyVLj@TzahTepJ zPiIZUk@In03&T@|#ejU0J>t|U9G~m-txa?(&068uA*Vz~4^B{=b`acIG^d)bEmC=Z zr3pjw-#XyveXd%Y2xngvLWFl6)JoX#pVZTI=SbpFp;$#p4LY_F_Lj>jHySM}>GH?{ z6oSpI^C^q1o?-QqG1KPY4(dQv1v z{6?*a?1UaP3o=n+Kd~eD=6jL<1mi1l%#e4Ncq)AaW8iiuF@1(G>|l2xJA+7+vrB{Ude3;Q^`UDathY6!eFPwN)#Uf4_7Va zks%}7S2i<{MdV?ONYQZ`F=w0+g;tB)7ratvrC1Eeb`-t@78kG`A7l>;0lX0BT}Y9G z5O&kwTRphx%k5rR1-YC;s5UsqI%vv3q%^4*jJS0q3X3pi;~BaE zx??<`<3~Y4oZ+7$;NT;|^x#O8D&iT#zCmnzWE%*CZ1anYfN=D+m&^}5Prxh|T8u|a zCC63F5`#q9i$ipRd>;CPdZl6GQ9{qv0(5%qa^Xd9;sy8KXw6?e5%n3R)ZJ=?=Q{x0~B#dv}Z(6uHw z6|5#L0BlK|{+77DN72?OL?zUW0XH?SgHr%!<97y93X2R_=_(Hj?6iA~vWg5AvhJigd#)r4BT^}2x9g34 zg&RC#L@s#;kw~px%HP?K1fpMrl8gB(_1c8sjb%?ZE~9@gNYn^$g1 zDyXSCH~y&h{{W#Y6JeAHZ59$}gWSl87a(~O7)cDrH3p7P56MRBf1!lq2V+4(SfVNeE%jO!%o=?8A;)PvoF;Z@#9b3({TKDio=hD z3Oeh^nl=b5hq0OCNaNI$h&|CgY@DaAI(KV$hHFc8 z<$O_(f{F1QPx+!F}uK@{|gBQ@Sji z-209tcX%|01?X{j3lm%tJ8{sBBPd7#CP$<^vJ6vZ8{(-^M8v6ka?PkJWZ`H{sw+|Y zjwkrzx{^YctW%*9Dk%auqGD0p(g)A1ghsj%qDlcNUeRQJ88ahS8fgSTk9 zCP%{L^hBphc@&f&LwVG(?qS-1*ahem#Q+A8?T05~Dv2Oc!&rL9b_p>i zjf*2lwDWIXWJwcg>^Jwg%NP0zWIb-}G9dOhh?Zh;~1N1SV zc6GFHJGw`obg+={Cf15xpN;#7@Hq_!-s7iqq{zAzdSxxK0#Vhm9gIQ31ZEqDD3)iZAT^v+AQ{IiM391J#{%4p~}O8;vKJM#$8X1lQa}>9J%*FpPAqCm?hgx zr`LJS#g=yV_RBl?`M)=(vX35HRj908l^ur9{*a8NOfmkzoFtts{!+1-j3(oI1|xU; z;B1X8x-E|tJ@HF3Nn=*?t^D}I1IaA>Lw61iLSj5XN2e@kXv#{)!TCwaf_=*q@I`b- zs|bq{%U1U$$?V1JD^kk&)8ExntLY$(kqD=kLCcYiJkB!83J?K&Xy=rlF68A9YyIKW zk)QlEP)UssAvms>4d$Y|mCP+vinb^^% zXXhYWUVbJiE^&|_acPqpNdOuLo&-;)11cNgI)xrV9@I93K&TG>2l*gj)#3Yg3Aw-m z2c1@t?BI5?ydTXgRrfg<z2s5?}*l0$ez0{C$p|@Lrc^8=H=~cnpU^yb@Xgr^ch`5 zys_o1jZj-4>~Nif=s_rsl+}Gmx1AO;#Y3QRlI(>MgDuXfyWN4Z0)8q#( z#+3kvU!FxgWMt^YKWNn=DT!(kZhU;@aNI_-lxrzv(<=n^fcj`{cM=-;sUU^ zJ7$^p*W1QiCVTs+C%b=Brs>!Gb}@f(t~N0>4!JnoFy$<;fY{vCbvwjq4F%4)F+m|! zXcX`<48*Sg1(86Hm+jda7{ZsP*vXp;_$v#n z6Oy~o#^dA{T5;9M!DOwF4Zqfn|EHK~(OJX2ZuY&`?DEHj_v(DZF5I`_`F&X?tXZjG zEr$L((9e#%K4WD4?|=F~$_qYfYmKJ8Qtm3QBCv{EEWl9Y@ns|PmHZIwf^WNL3bES` zLk~e~C40sBbh5UT@rI+kQjb>$hH;5*?%DL2Eu8C=$2ygsFa1q&`{0&k+?)}DLEH^i zBeVbrAM6wgfs>VnuN6}Qqp;fJuPb4fzcD={5hwg-z2>Ts?$>Wf(w5x)=6f33^K1ia zeR~ili7K` z2??ht+voSS6r7hag}^RxqRyE1hPiUj^3zgTeKYbpykyFB-2z| zZb{Nk@WEf1Qp;ox9`22e)l;Ezxen5OyoJ*nEODdhM?6wd!oO8Z@G+ z?B)yYT7It4H0}lN?LA(k6(4qqvClMx|956mI$u&i-wc343ef?NTEhO^;#9{8o?ImJQytLF> z$iG^qrSS_UX@$68JBZy87K0j}3w0_d$F$jDLlveEhU#Top!FmF7`k8^K2T^JL^9w5 zYa_Idek`5CamT6RVdKx;my|QnIJ^`CS{w|uD&U|a=i;E5nb%L&u1((>E_ZF+;F&=I zG6(<7WbK%J^pO0`Vy&ZqbOvH&xC1ER4j#`uG&+9aM=qD@Nz+~Uyq zEaK~Gseyd~5k{}0Bm&y3MxbP-LcFNJ7p+RlDJKCC^O+jcf>V+d)I^#+dZ+{h!_NFe zK93Y=IhnC-BLo&dI7ORX3*#q&=7`HU+r|=Vlol887fZB?qmELIo2EPWoLjYUTG&;! z6}LYxbdVpEoTKeK29WOw7oLcwyZP%=wLAFNbuEVvOwo$yju^M0yI_G&cQuS7#CPOs z4t||ObEQt@j>0-Jbaz$_(3yn^1}7s zPE);;@4M5S%Rfjrr{#&*4-^q(r?g*H_y@1WF_W*O0? z%|~DSAw2|Ck?;wU&fq3~_#8`-nX2#|KR~+D{`uO3I%tY?C?%l>w1r=b)O+#41;a}{ zs_M~8DmA}}-&v=v<(<`9RWuWF9Ec=w?yuHn^EWQi%5#NNeIVS68&6!dvvC3#d#;q9 zScGpfu3n(!^Ix1{$)t4SmD&(*+MZNcx;F8K7{l&YcOGFmdFfxQMf_iz&Do{np~NAN zz^QdV{$6qp@2bHy1!v!rT{vo90*O>{h(A}WIgWl@qxm(t$j)Tku|T)B@%~3~q1rgh z9ldQWc^O@?yNzomTxOa+OIySzznPqoA+igaap|?Z!>{H^+***)W3uzV;Bq$Zo25xzgL;8oSep)|A)0o$TVe6W`hbk=V1FDg6UNNEDLK^JPgEklQS6fux?j_hMsI z&DQFMjXYIVS+>GL3#P4de#JXl3cAOmu1?m8LMs-jFVY@DG|5$6p<~@dq#>>`bjA)t zGZgxxOQnQFRv`a^I2U*mGSw5_k&+*-H1Z~7aB_SA#0_H{U+138U;4W_?dVmrwO2F~ z?n3OF3t<*z(e%+D(6FNiSK|_|0$Uawgzo*v9+5dvzRpeb6)0TG+rDZ2s`lk;H*6%j zCv+G*-)IlE8~?Q*`Pr1{XLgNR!}Rg&ni`1{;~|EQ|7r|r32I-;-6=GYgrz~aDVlpv z>w!B%@p z7n4#fW_C6GBc5QQqbYtY5fjGgZm84aJVTGpm%8SUt_Ju2_<4&ppAOut!u5n?r%`|g zr(Cd-PQK1!ank2_%)_xrX#Av&km#)HSrz)M>X}$d?8bNklIoPP0AfcPb`$Ow!HHfN zZ1Mx=@(QTv9*|_I{EqrC?%)@;X51~9xOl0D^n>^%rCe%gy+}4Tth06g1$EHsHd?V_TpF#x*+rk|huAD^>eRr*7}!rR`d; zR3pEdY_cD{y8#Myk)U$rGZ4xOfT3I4kogrAh zo{&i}T9K>Bp8H`@3odi*qfMr|z@0+4RR~{1#A8%dHigV}aPEO|xHYtJYXBwL8?qam zz~N1Urra6SR5{tm)oK_>;GXhBL*hnsQcCLhkL_XdP(Znnx;DS1 zvLM2eO2SYtMHWUiZ@Lc5MP&@&xUIMWI$8s|Lbb5z#i>uB14T!#FwR-y>p5fw?)1a^ zO9m|+6&5E`jbM+EX@;m4dOuJUz0wscHjIy`s=EP^r9cxGQ6i8i<$_`ykqfGC8LS9Y z?5tSW7QOEeXmc(Urmp&S$DsHO0y2uk*V?IdbG+FBNQ7+HYoSHSyI%un!5^#FiibD# zBMl3uA+{kcLmu!{Rd&WYja`jr;MnLg+Tp_Z3u5Es9(jX(>KK@#ScFg05N=r-4bBIWoNc(LIxv8Q z3>^L^>j>RIH3%a%;zV99jj$6Vh*cI}x{4B@@p>U%Lx$oEK7IThPf1G_=qAPE$U$vg zDpLPR)mZP_>F3&3Q(n1e#v<3&&T_Y~xSb9PvI!i*ArlcFbdEMC8?OPqk5%W| zA+3a8vsi1)+v%t4s7PRm9_R=9<5^c}cUqMo5jVl-kIM{Tuc7ADEGO8O@@eap>k&!i z)z{%WcMGo7CbQQEQnL|apqmvWonbo%>dLtHA7D=pzpq*NFWO8lUfqv7`t%)I`cZ`A z{S){bndXVS`imyZ(e~9^9$gvWz-9g5?D!*ugB-YB$~2u_`ED97J_kORmNT`yqyOBb zJ)d;ynnzLdHx@JYOoX5KlQsJ&E(#gY_>Tig+4Ezu0kSNN>y^oTRP-5pNFp}&i^S_B z-g?@#KLf+jz}qv{TAwL?B{qFo{W<0Sg0Yc{S}_TTRygAAKl~C zx-5KMa}o|>Ngf}AQmRJsn1v8&f9=w~#h*$~aUEUQqg|ZLD{D0?Ur-1OmXwd;PngNW zCO0jZoxc~*Jp932OKNft&aUEO?xqiN__04&Ei>pByioEnvMHm8R8I(t@=t!~JIQ$y zu(HcV7rb~X&)Lwgr5^1FYTJ?v#}%ACuz#X*bIQ@b4rtSprnl^d zR~PrILVs7^K2S!Ih>!+D-q0Yzr0kjX1)dIEI^&V4c&@(A*)BR-gD>+Mb}|K>$yNr= z$>QS=Z?jaUBa~_M0q3-5P%BHL7@+Y?xM#9$31Tv)>ZKW$5J^b>l|gM9auWnwrZmE( zB)4QB*XRyID?6)3Xy=9`l3^muLuyyhC`bwk^PRFj@d6=besWODX&%$RiZRJLL{BtC zETkTLEr^x+_!*>weuzD6H`FWR0CQIi)Ivg3A}o#=YeSmucCFwe-E(eWSFz zv^Xu#%}PVLJZVU)5DK1!Tmd##GmyRxEJzwhP3?uB$V~|xb0TuFp-E8W?u|#1{w{}XvzQ&@ct*DgA1|d zIJ{tI4r%Alm0#;UHFQ< z#uCoI*>}^Y>DyR(MXIs&$CNMNCx^7^+PGOn!LMl(22FG_@xN-@qDOuw(h=8X{L8pO zcbcKYD_a)Mw5U4@l9sj|NttJBwdwrQek~Uc$)<0a^47%hs7`PmV((J-HLit|mmJV7 z2)F>zA0idG$}@v}hl$Vd2)y%V-8z}4mszdrWj6_D?|kve$hZPXq|m@!fyZ>4AM7?_ z2jjgE_bBqLmy-+dX{sC&Sp~e|PVl2gpFy6P-kXxncYZx-YB_`vMb|K4I@28e75g@> zkoY_O!GSP;^v$Fxv&#g39>f=>`fzNh0~cXKajMhF7h^aRRA(q4hGkQjfh}Y)`D5TaV2@Ht%RYg5&PqJqCy%k(>7Yn*S~e(F!$Zi>rNPwezFebW}!Rs1Jy z`EC2OZ7v&la$KJt;VdIY9gh-7RsNTYAkOaCr!AZ4!4gu9STkHiZ!SH$^J1;Q!uL05 z6OD+#6_;qWQw@h0-ORm#&cXQ%YYJ!)(UJxH#B^l6T)soAnkdDuFdbTOr3!Wy|M(K^ zF8<7=+H1)wzy0uK+H2!g+_MnZwcLNXHjlr5xfbN-y{Dz->yXmiKRZK!(ABgrTfVT`Z2$wG!eOs#5f1C2*POI!Wv$ z&W)jZOMFj^R4m9)yXZ%qhLpbF#H!SlP-~3EGxTpbhR6j(>mqkP zUw4KnznNS~Vfc!~QW$;{;fW&S6Kkeo-Q>sW)4g33*&$z;@_wO5I0go_{Gw3~C%lOF zHcdYJR55b_ki`EO$tL9gDc;4T9t~e6os{cwJt(>B$P5=V!l_2JC!IX=Ds37`GmA}5 zwm;Y(=RKNUwp1h>)0{;gqdTZ0Z&JK7>91c)F5$nw4Ca>QYHd9)FMzAR;ea+H3l~}- z;*Izn#YIWmiSPLs~Y^ z{HJDV>hX1KhalD~7wb5XihrS!Lcn3A$n{lOiThzX^=Y*ZO2NW?;9i_X0Gao~rG@K$ z2rz!+3fRce`}Zg=@sDdUqI8rK09Clpg`yafQwd6yZpD9pwdN|25aX+D4*4;5V+Q<$ zIVpPsM8Ba8RPA(nppQn=c23n$#u?>X;LC|GAwesWzlm9p3o%#B+^S~Ica)j(a#VjH zG<+;m9`$bK|N4iPZ;K&_m;O^Lw1}Ew(mC;yR%5vNx%;$ge(Zpjk%SL5@TT{)De+z2 zxnIjqLbUGsLt37FG@b=0{GZoqt1_b41I!ZY9RBjPa2O8n*KRx7b3n5uO_H=M?qx=V zQCL7g7wIIprTKcT=IA%C)i!9KRIN_Nq}E&yof@CMfjR~qGUE64HuusgcBKxIrS$)C z_vP_bR%ia7ceytq3)y!PZWg%NNf1$kgoGVr2?0S(AOsR15|WsOrGgQ+I*wIogr`~; zsMZ~Y+V+m)N^5IttxDB8t*uI1J8g9YTD5imeV?*T(lA=Cy20Om}B+zZ2~RVw5D!#SzXhbn$=}B zO=}Q#Vs%-xP?mr~qXTh92_27*_6J#K3XnuQSSi)c=OTuzoCS`cPQ|ul8B5LN$&;A)VUOkuIg{9u$O@tq zI{~pejO0a9KpmVG)Hx@};L#BS0O^vE#23Mp&<#}ZJR!)J5!)z-uD=~JQ3>K8 zK~Kn%W7VIqexU#vccjSlM{QC#%zWFnHhPwf{`fHXV6j?pl!5`;{taVl#t2}EV=JM< z-!P8Qb-RuDDA`*Hy`BKXaKlZ89ak(T%|}o|w?X%30k)|AW+StDIB~B-(V@+#3VM1s z&KiSI*&^N$-)e1o0Gz;pEleXMik3MQu{R7#>>bS>lB~FQNB$x!1jh2~IkHi>r2(_? zigt158mZlPxf6QyCZn+K%Nqh{t_Itw4FMS4UVqfiPIc$*>HqPg_V~e(N_c~CMvO4E z{3uw74?0#F??w|xViImW>}>?GNDZUbVU;xKt4ei|G9Jp>4|Vpxf756cuu!OAfl)~q z&J&jAjBx@n^yE!O&O|&+h>49CK;w~Ohb=5o++cBKDa)8rZLUl2++dWPhj1;(wN?{g z^9y(7%3=rZ>MV#6{I0N|@Q@EozKvkdBKI6JDc;Kwb{QoX&4Uydb~yf8g|3wGYG1@8 zgpmpFEVf;fh|U%x1PWbyvyq#?p{o=&;fOi($sdhUL|nm%0k^6{bNJg+o>?VMYKVyc zE4`;69{r*`vR_HeDk?WZ<&($!}8nF_tYmCl4(-jecTz~>;0E!%h;GMT%Ma%33 zjSTSKPpg(Tp3}0Tan76|-FLN7z^T&qJ%g7qc8wXIAWY2Jt4D?$cO;*2G@%nU zx62lw(rJgaQpzr#j*O4a63(n54HoW-m?azh3)5HSfvUYtnJA~g%SZ`V^Sqf^#(#QH zpC=uCub~jonec&;xfFUp0mbeo;hqFI)DQBKQeF?%mcv*OgfL z>5N|8cqLBRi~YU~N_@n~NY8q>jidmR1vJ5~Ny-$QuFu0ibcUNWy4~ z9lGr)O!*Vkq*V_YkJ{W9Wgi0lY}Z(+V`d>rbr+D{m4Q#YotA|nPoKYLNg;2_kE+v8an5f(wKtc ze{3X#S|2gm%qZ0z<)5&#=N8F60leBhdW6w%U9_L9up}E+g?fH$r$hH^#+HrXQi}k- z&}^S8=%a#*)rh-lCXw-&krg`rsIfOTbl@4|MVndve#M;GI0f=Qk2^8vc{o1w zd<$rEQjX~QU^`Pd@zb%tVjlpPnIi9Bn!=0Wm4=S~G(Cl(A)+z9r_qe* zz_Gjj;fspulU?%Hp%1T1jAw$xnGXkFNM(x}=mghqZbfK8SzD<7)h8w|l|eb<(IVRU z4_`FhH{Hrh_4K5g<9-}TurpyIrmju{J-Ev}n-X*2GPCrrb}Y>>5zye^^TtKUXRj42 zp>Z!5Gt9^kl(?&`rIkF9)Mx;ZDubb(qeh)UzkAuJp*6>V*nZj$fzJ(Z!!~8{%f^(@ z_?L|54C?p~Bl(Cz`t2``@!YyLOGeO|1o{nV zN(JAU0Yjnbbnri5OttrOJYl&fjd6qFcUISL;@JLp#rv6_=W}uk1akmBD!5vzGg*;2 zbxKf}?64Jw91u5C7PM$(j4ttZT~b0X_4)%MZ*4VDI@|!j#_O6Yn*>|3V=p0@5@A;v z`FZU5>DGTYqUpg`jfv$VTSLT_aHc}@HsuNqg5p|c+Gr%>HYT-w)<*wOQV zA@I0tV4HNTkkR;TM$sN&hE}u39JVAE zjB2!ERLkkQax>H7i@Wv@Mx~CCFl-_W&R#PdNh_a<$BY0#Atsa5HeMTP4I4T6l4)bK z{6^a2(#jL|*umC*wiW6Z(xJIZRHo9vc@`SU92L6~FUP_T& zOr$It6o(SdXUj*FlLL!~R7OLob*>!_g0mH?$SBy{FFH1^B1}l(1EG}gCq)Nr#g z#wXYus{KzReXX!wI>pMh0lIVfzuex3ZGH9OX8W>{wW!t*K`b(4xl_Bv##Nll=%stB|&t9?7*p&}co&~u(40O@QG z%y*PCG(wW&*)-UX0)m-DP>}W5TSnGAb^7dzA-phPp|GhH(zEuqN`!5I4VM4_fT@#< zT8L^`f#5xBGXH2SEFS@sAf>?*3e^Fmf859!h+6+>WDa^gKK^O&b^7v7dd?|bS1=u3 z3sU{CeQ57GcRnhf&;Ja9mFN`_{M=)QMR+=$DD_1Ro@%dg6(Nv-9J3R9)wV;(!T`Nr z!T$HDT<#sP0~eB3EMi+L)CH|7ZTPx&w!(vClm%?Uw45(*dtX1Cxn*1lVaZtN_3(^Q zNe64fa2m{}=E1n;(qqPE>iLUt#%zg$@p>u`Qy^%jy%{m%BkpWCd!ftt<_M;PMLo3a zWoYKVS!&0P=|Dnb;T1$j|6-)YDT@d^b3neke-9-=$M5kNfBW}FG5>m%epG6wXN-vZ zz*T?k55_rTr4UC4wWnKO0_pSaAB_9y7gHhfI$jEjk3))L~sj2D8623 z54`tMh6<5#QtJ7oFLkgoumaZ*;dn;rF+3~}2xLyLqNgDPjDkoQ=;il|3Fcnqb|N3n|KOOp$QJu%C0+p5!_`Pd$ z18YklP!WFwX?&%bHc5YzN;QP~hTO-~3}Lr)jeh(NQa+gLG5S-G{w2!%DzfT{@ECI2 z8N{TLqe%{gOaMp(pkkrOI^Nl+*Ko9E{3tP0k2=P>Ubt+q%5{Ws^rF;!M;J=iP>sKE zG~R^Bf!J?`zmGnOr_~pZptzvOK`@3Y(VH*qf%#!C_ug*3_2J!sXqzO>(uY>RoJ|v= z&6HH3ZH+h&s*N_glO^Kf6A=OR!57h!(dLcx!h3cURlIGCP34#^YB!y;q$C4=Zwo&) z7E)b|8AWHjVkCuL9b?9tu|-g|L7ujuqrEruhgfrw=>^_uY9DKU#4$pV3 zej(YcPw`G#Z8>G7m{Tb+4N(-Hh=-2-c!GIkshZrnfsW3$1-R(Y@Y!()Ic1?q_fRAB2m*H8rw`K^tmu@WoQv2m?$70G;?-^mw5a(i|d0yzp zsb;4oyqPKeIwQ|!UmmKz&6t3hQ{LUtqO(r?~oTu)0f%_a2rtHjJTu)rY!oJsvRAAQ2BRv)tomO2B_RQ2wl zix_iU^>-u4AA8twv0^}e{L|l!M?BA8@^$db{d~xXhN0^9xn};pB1nJhJ~Sqz0uUS< zRlObbz@O}D`q_s@k-2Fj!&^gw++V?EA}_*531+ZXj-_`#GG3y4^8m8X<(WAU$sRWG zxL#)fw=`9m$)P#pO{j`$^3969XMy5t{2a9IiF~tx78IDZ_*9!9flkf>)~n9zd^|dc zWie9YHRaOJO|v?5PLVmrfT<6{{9+3>)A%j~k!hY_CdF}Oebbx<3(1&h=EXL(R%{TI z5QH#;X%JS{!9s<|!c4FFsy{*a^Tvw~RKMK#o-D`LlN0%+E}@Snn(H|r&gYAcFv-GnxkrDkCwcv;Xet|dG*lp^Gbpa`V) zrK4{Vr{V=yK`hRunC}_crQn5R;7h56#&zm!g+p#|(i(OksA-*)S9jo!QwVn7{ur-U8|8e>AAl$z)H zgekJ*Fk}D_hxwr+RYrc>3Kk_hVfkTm1~nL4pCOQ1EBNJJkjb#=@hBaHh&cjHveyhh znhen_U2YYoV94sWZeac9_I~<&8fN#WPe3!a{N9KOZL7j6T^-5(8ncGVH<+if1}~Nt zKV!s1FK_38`**ndI}WK+gAhY48xXGI#RuT>jbJu`(6^_V9~wg!Jbc<%r5|aK(pWlB zj6g>{4+8~uPdELc+mLSCnC>AbJ-g?nG9^F>=Ygf`1&_d1NkZyJMiA0b7brffHGfYD zb>@X8AoR@{W+m;YGyh#HJKg@Bv!e2P^Q}0y0Qd5`Xw0&5rg>oG&lQw4%iK9y*&DNf zalUev)WDzFGn;(FOa1Y20u+RI`wolVz? zN*fNvuKG{)0P!jJDY_6Riw=MWPtiR}J!@k|j`rUJrQgS$;Nibp2K5ahyZESTF7Tf? zY`sa@o+G1yWk8=C0tQaZID))lc|>a9kuMF%Lxg1BGYQCyWk-aLD^1Yv9GoC4$Q^vq z2~$#|S(2o-q!GwZD)mD%8_nCSbI=-=b`6W_SIk}7xVWXEe%3kl^X9ZP&Y3soJlr{O z?|4^0IpNOqMmCV?Q-|gG`cXoEHE2)q^b1amE(@GO`$q%lBsKvP)l)cjYSxnQK zOUf$XJqZ(`<15Wq>-qdS_|k$UD_WK>xL}U(Za9<0l0_J5>J$vjlKNRHgrCAj#X%Ix zNzmF=W`Q|%3SG9!oKO-dmNVns&;oTNUD3^CpL}Scr&pP6CZ(TmCiz$_HI0H7m=Bx| z-}+9DMZkg;Uh{%k=YnhINvdp{#X-z;Q47AeAn5Y)1xuH}Z}`cTX4+uncMRg>&w!$b zLw&)ZO)jSy?)3ZtG%0+H5vdeEiaFaJqGLhS)4dK!3n%cnOts`?}UHE(){FnzRPBD|M*o_99{pk zIf1&KhEF)#BoJ&83aO#ZtP0IuYnGdZr!)|elal5gYun6x#07&6NQI~rPp$-AHwLKJ z&V+K;nUkYb6RMu-%L?_ZGsjwI3v9y=hHUM{HB53a*ty=zZask>sN{Y-ho1ikGz_mc zN}Vp%KaB@;LJnfq&C0e)gyqbCw43k9H`R>-*zg12#G$~$V}>5wkU}DY+#|*28MJhx z*+@@qG;8UPD-mbsvyIrUv!mF^VG}4(M1o7C+)ZY2f!r;SLSZr-SO7IhYuDaB;wWDpQvvmO3bknjK>mN*f?}?C;oM-bn&4~-s#w5UdghWbo+3<0(V9F@hF&Er^VSyI z-qP3C*}~x;#O|U~ekS?Fm?izizC;6{T>0(|KgT^oQ7MufUU*(qOooS4_Dpsf?W}_d z%qvl5;(Wo6U`$Dyw@TSt@sL9sLCS5SgXK=EZc0R#HmFgG#Jd|dbXM4$2bo>8JBaQ zq@Njfy$jOPVvD1MI8ZKZ*5sr-Ul~EVe~K^^bmDe1hHkjuh|%&S?+q=c#Do68Airg7 z1K=_N9Bi+DQ!B!{u+_Z2jmX)hg!oLP2vr$t;zcA|_Mi5IvIgr0xjXBz8r1z?f(MMv z^M%cdmd6VbN{SnJiR+64q5M1u9iq=RlR`hM@MEQsdqlsuz4WH-E671Z)Dr&|tOY-2 zuutH;5VZeYC>1%^k*t9tElSc=IB~kD8x+r+xMvD#!j4a9OLZgf)x@_`+cW01oYJ6? zmc4GJBS=Bqh75hhekMB z+Wj=-4VO-~Vv2hO;1qixo`G-pIn$nxjkJXtRMn#*4$*}U4U4Lj5XwnY2t&7e>rocdSBTi^oN{ zWr#PiJTMu_(l~|--V8N4J!bDvVnY^9J{#=kS$!Y~0{x~xN|Bv(L7!RfSYtXiPq{QF z-bxSM(q~!*(G^zW{BUSvL)pTX99TSgPiC+|P92A?Q^1`|bE7PWyw6$G>sC1gIXUH`B&A zqmU70r_f+9DvDY3cQt|IUdU3h$Jh)0>~gAl1g82J_X#N}Lc?MXoa|db zFCq;Bv-Tr$H6IhY7GkoZAXGr&H4Zict~JMk%OsI;u!rsDxP`6;!21&W(8TRm)9a7> zG6%n4H@KpAGrY4GteKWpsO=Ps12Br+gM}LhRmEDBI8JPgU>_B!5ul!(Wr%P`^tXH{d zBf<_WB|&UAAqcwQyx`)~(T2$C0@t5vul4w+YN@B3r@&y8v4oW;vbr;V)(|sl_wKei zRYzWdV4MW2uyli9Tu(i~V3%4t;$jLjaqQu+TQtf7Jix1$tXHfrT;%=^>wB=E>Q+HYoo82gra~d#&;{Ws&_HYXhT&d$?_5entYAsG_FXFH> z!LDWu9KiRU{hcN5fs1u-f44{;!U6jgta2Uh5Tpn58^R~*lw;F@ufw3>gHNog6mN$z zJYNY_d}?JysorVvr*LUF^mRxJyFRt521lsR5F#XFV)fxORzfS7?ho6?kRk^(FF={T00|#54g|)M~OPaN8}f40O^1@&x_{j zSmJ=g^)l6RKJ=Jrm*y%u+gqFabgPv6_P~8JYf>5QyyNh29(Uv^ZiW8oxYO={R~W~f)xaa+*-iz7DCSV^Fm5&CX%`o`LTktG1iaWPL9 za@^sHR1gC1cuqHZJh~r}5L&+7Of8gMy6s13PSshk6OP{#f(j!emMkj7E*Z#aLlq(CkP4FuvAw>$jE91K~3O5v+;ibmep z!SI80YO9W%n*j>6L6aVswtk#WH5YzA@gR>jQU@{r>*xpC`WO2C9Ei;iuW$boyd;%(Yx0Ujz!zTBY!_egI9WXPB zME@=nj>Oteql~YZGcxs_xY7Wg8r1X4#@ZPKEYKhFS5`pd0Q zJT-pBe3=d&v=TyP7n_$E#ahsX#!Oy`Ya`mI z3Gj+kik@m@`4B03!fY90AUu(Nc5Dma$_lkrlWW!?LW!~&1Zm> z95%iph>iA<476^9gO-T322zEiA2B4dF&9TF!tbul7+4y0ZGjS}bu$EO;;I(4(b3-_ zvea|QzSz_GJn>b5u{iCDT6VeMz}yOA^mc-Ykx|)(4*y%uzH5C^>&3B-w5whN5%KO9 zwR?5or`TEU&Fkt&FKjo9COC6(&Q!;a_o#VAOgd=`Q?SE4-^bZ2cFyhbq z1H#a=bNB>WHqn=s1Qe~6@trX49Owzxo#)V3&Ngyv1lA7Hk%`eoX8~qaC$7>Fuq*vJ z`#AeqbFeRf`f!_5<|q;K6yC^OxFdIXkqGt zhy(Jp)C{ZFMrTw-ROo^Gh)^|kbh{|4quYHd>9$9G1<9hC(AMx>W>BOL9`%*bj{;W0 z(6oDE>C;z?_*79QjddWbkun;pc+Uu~72%cxXXB1;0bZ!RSfB*Kmd^xwSi z&KBjuezPlxjJfRyj|k( zEWJwUh)NoCn}QtfLP)@s_%+B;;)ISjh?#Lx7!biy=eq#|2mok=c{I-wW{fdVYpbL! z5tSs=pUz;9T6nHutS_qIXSGWIFhY-rR|8emkpN6zFRzGN9uzhW&IGZkn`ZRfUN0u|b4TMA&r^!=;8p z5;k|K(VUy>sm8(C`CwagG&a?h=c3X40beOqU1r6EHvPa{<(EN`x}NqG(u+Uzjio1l zVm>y(OU;5ZljrIE$RuuP+fU8kn?%o=NlVpb#JZU!P!_I=(NKZ@gHWKg%W|nQQF9X@ z5z(HOKWF|fb#otR6NJ_W9Srv)>>Kz6n(}kAI!TqN9*|Ej)b?}po5pH!|H6181nJ}G ziY%3oY_N`tC<${AqZ@6Hj-^3(~(GGLLI>0snUz9o^we zi!M+z7LK}pnIV8qRvKwp?Qm8{5F!rO&yE|qGsqxYi>xerHcvFE*-8OkPpax2;0haiT4#lW0zCJT5f! zlDX7RPy7mciok1#R&(QP=Hv{yt?h_j3t~bA48=)P!LQ9J6!RN1b^NaK=B^2S@Mb`I z=E?&2#Yr5hlZS`a|Jqz{q=ogvNRY!p(l1^!J5ok!boOt|WyamLubUQp^^kT4TezMf zx}~iHeiR%6h4C~zR#C>F3bVjEum$0t18{l_{{mE3_pX`e=VsF5r-jb@gPE8Zc~|M! zn`Uas5*7t3CKcD(9d=lC$Wq5855Ksgo5>NE)$L|@1I6NttMk`mCJg05_x#R$-=weo z);tTEqfBw3GQ`zTw;5X_I4C~W&+avHW0%180&;!C6ey>p1{0~&dtiVH-FPZ(wd|si z%i%z^{~%HtuKgIMz7zA{i&A9**}wRf`8{*!_7AfiI6<=eKbf0ne3^?S_nys3)#ZaQ zs$;!)ujPGP_h&Pk6_EPl)qge@(4St1hhFo`W{d@|RT>{M(sLBi>DJ^MCI6xW-vUK+ z@yj5(^FKBN4%8oui%Q+Qz@L&Sm{Li!y6v%l^w3|-HT2#8f^pA`cg*NSCVe0ZV=e_R zBI0UH`Wu{!ZhjlKf8}q(z3JMw;cB+|ZSz9E?2KM{+guV?UvYu7PlWwDEqe#lGdepF zUD<-aL2c8L_t43vKR`rr-hY`1y6M_Kz(M1Z|1#f9SG-RcG)utDkz$NX-$Gop8{Rbs zY+eW@{0(Z>oa5#gj>Cz#b?@TOy5r`;WKRTJ4hY%Zi#WyaW0V(9;OyHzHw*moGwuJ> z4A6|@Sk}@Lrk_I-;g}pB630?n$qF}hZ)xvA+$ZczP$Wm^A8Mqcoil=B z(B!GAS49>yc`C?%z*dFzJJRC^M8{Nuj@Z}OJnChdM3FsaM^bkzsga{M`#{`^;SqH~br zeSC)bwC4k}kT$<>PEPP{a!2oay5)U@#wz@jowWW2uP^fhTo3F*9=-dX88fB`7*y;5 zEqV{x@*eyfH0qcC%{Xq z2SKsWvCmmzJ| zG$VBSf&~?TRiR=?qiP(g6bo^}4GMQFgrifi$B2lNM6R#J)swM*0^MCmAsI0l@Ql(z zT-_El5|0PkqgB7S;^~>A%|+arBsrF*o6!R$Wr1(RX=KcBTCQ%=OAr7hB=&rXgX}?3 zsr2wcnaUgJLXFReF+jh53K3YIS!9i+fgk#_NF%J+0;l&;_lT;qjg$-wgJiFQtX(Yg z2SNISTgFqI=yVb>ML}UQl_`)7143pT!vBG}5>Oy+RR-Ps2JmCc&3->aKs5c^8|K(E zoM#hewIIFM_jZWjks5D#x7u6G=rk)?3=wwXVwc$p4_gZkg!@~^Keuf{RtAJ%(ZjJI zazHdkj3~Yf`RY&5`C*$cRwh?f^^Tdb_jg9JPeQi9_Iu;3G!xI)RY^urzb9XdvvL@5 zW+f>iy8=SUbse4T9J>EqU>7t>NeR1E9Dlxh!_b~*#?kYO(K`PL@R=zn`ylEzuGC}5A5(RtudMm2x z&+0K@fI^5n{vUn%j+qlO|88zD9AtImL-Tz4@juKDykCCV$dh(u*kaHqO4hV<5of58pPEqrsyO@cD#Jm{z{Kl`}pu^WDiLgdL!Kn+oSRI;jW#*kXV|PFNA(`BG}|A~69WUkV1PHQr8LR0KyyZs<4$!A^^3 ziLGa>r%X1u(#U~TWeG=Bh4T1_W#s_rYiJE`g;N#tq|O(7xiqN;(Fwqc<tHAJyR-r3ntYY<>9*nid z%g^+DtW}|YeiCafQ$Lr)S*_ImhMBSq4ndGb30wn0P#ZzH^AN`64Wbfr{ct8JMqC1< z=ySNTh#u(PIO|q5%)EH(HwlICDus(tVbhube$AkXW38Kn{Zsoz%J_<&9BZ8y{wb7^ zU+7O{b?=pGo5G z$ff?kY;8@+yo$U@8xgFn4-v}Lh?);nP{*1=`NFFA7FdN2-w48Y^%h@r7Pp6WnfD3Y8 zadXhK-ObFEAxC*wu{r>h6(IMJqu408xC>sJyP7#=l5;kT#Mwm5O>CvUyIu%T1$s|V z)VF@itSxOI!rOs`L)T_l-Nqn33mvVXsl7ZWBt~1>7NIV-26U8T4yKOIvo6N&8jw8h z6AqkV2ssJ%qVUXt(K@9f7EPiY2&D?RXjwIUP5QE|mxj)KyNJcSaB+7c1m2!)-9U94 zeVIcwyYr+3gyWaTHP^FO$LxVEJ+LxreHeZ-h@Vl+h&z`~&#|_par^;~+@np@4Beh% zAue&wX0tF?fq*PfAn0^TCol5nA&4R(rEG3*E2j8d>s)%hz)GND;*tzIJp|d$ zX_D`OLI>lkN5y$)b%>ep(sFt*4><6KT+5~(#z#XAvdu^yTg=fEx? zw7tjdyg?CXS8IMM#k#kF%#{YOZ4f%JbR{sks(Nxo)matQr-PKiB?V)^SGjMXhfNC* z2(-0p2S@j;3^Zb)1-d`AOfKs(3x?j<Av%B-?Zo3aOV9a$&MY2 zfge}n(xofl#|1T1b!Am0SOV5siQ33?7NZ11BlZkAZqY~uV5=oI5b7$$^o1>mB`KiW z#&J;m;XN?ufK+=k6$uaaej6f$|LI(1hb(sUq!h(>EtrWx4v~&?9ph`p?;7 z40>#WwR9qIZfl$JHC1SL1)I1q_a-hd`!cr=6%Xc?GW@j$Hg~}-9oQN`$VeF)$~GC2 zwsjrfRgPNQKlJz$xs$44NZr@5zG9#cQpCzFZG{@1EBKUz1&;W^BLSs{#!R%PnRSW+ z=Mt~;m0*KMEK}#9aTy&gvU2kWx>yY4nk=l}JQt&g*IBAl>4i6qj78!Ey#S8WadwF} z4YY!qiMj)FG;cMznSIkjwZ)cY4DBDX*#WbP4wqQRO2d$SMJ*$JC!scuR(Ub>^|Y_2 zr3=k*@cWP&4|P+xz^myf@_qo6G2s~2i&I%4dxf^x%?0xWK>~`3&Cca0ni|o5S1eZ{uLO~SIVvB2||F(xr^2bWJ}X4th4Hr z^DQWV^}IhoIf`Nms+!K9d>!yV$6c2XhlMIkE7#ls#Q_#T)oox*4^4tW#k~1Z6Y0JZ zJBA+K5}ibuFZq&a`Q5&3x^{z6F&Pldv&CV`eUwa757g~K|LO2`Qunn2qJp8fHsi%= zo{hh8O0X1RwVkNgRE=}yAbdF`&Ju)xPkUuMeE{jI-}Fg=iIg-o^kl6ym0Fsu#Gy&M zW9h^cAjAhMtyMlcbT*>aoIeGc(ibm`vd4xi^yu(m#D>1^JrGrcQ>+s5Ra;r{VcNQV z=UcP5%MiLe%TIr=ft>_mOT<&zvq=7N?=&kdmB9qa-HAUVVqqVgW?e&B21M_s!wFvKx`**%K}DNE zSI@Ank7BW2XZuddsIzWBTJ4AGtZ!1)Jj+hSt%-DZE1|1nL67#tkHEGtOIXIW#0Ccl)bN@Jsi@#`6q z>Pev&nsB||oQ2K(#j{xI+FESP)8EA{@0x8TrvZrBEO4dtl2R9pE}dV~=8#@0t=3DtRZY;Rk)91l#`PgjhEmOo%<=olW4H#RnnUa7ZP6`D(&-U$5G7086 zb(+I>Rl;w&!V^`seR~hcujN`$s&cA%dkC$cZ>1Q6MX;aiZB?RaEF-9*M?rYuX3Pte zfzm4rPo%047KGXI=6cp83GB#@LCkW zoo}r`l(DfZaS^94gi*pB3#`0YXa@A|Q(d=jJcHJl&>t6A@0%lenj+qvb(4Xsv2%%4 zYjyX|qDPllxn}cD`qdI^Ty#-)@4Vh_m{u-3iCnQ=q2e?tJ zV}Y%VR57fFX&DR;L?v^nHO&^k(7c!eOTn5AEVXvXZ^xHfcPvu_DQ%J4pP=Lz{Hq1n zs$O+%2^MZ`K$|n=`3=_O1YJ>^Uj1u>wTE)YTG4c9v9*}4Ut(o0;6@swDhfdUIay_s zQTIkHntuyL0b)0afX{C0$#iLbR0d@&vrb#iY=*!{vz(pbS6FUM*hN@#Nikk4mfAH} zieDlk&#cFU1;DCF4}ENtvA6o?W6MO*bK(gk)5BWJAZt+m6YsuKD_%( z*?kqOMXtW>Ck-3&OKw4 zN#MTXy8dK^RccJ9zpk)WhBhv<7TP5Y+`5-x&?P8%_4?68byns|DVXJhIWgCVnyky| zM_oXPi`H9xboCM61giQ0bc>L>!h)TSzK5EeM==-Ab9*nmD3ESQ+P3#D&|i=@=IEn# z&U9sAr+F}zf5?~=Oq>dNqoYq$wziI(6H@=BPk^I*;xu1Ovaaj$a&+XGD5zU~*0_0k zbWjT7AL-&TONE3}Hog!&V7Ag1qpdH%=GtK;m~s6NE~i8T(u{(kA5X7)Lf z_~JSYWKy@JW{-~;1?JH>0Ht(NvO-+~`%-CC%>DI=%#_S|_?*q1E|XODoaXZf8aB1| z>eY!HsK_zNPFVUICGD}&=KzKDcdXYN>TY0+FllE6e=~PfQ**(B2FL<&0K!08eOyuB z$niyJ_YG0$gUUxiZSy~OTmmu4z2P~qYO@D>QT>v_ksgeQGK-+!%A0~$0}i%TH>N$0+3AA{pumDiXkn8A;aSUPa6ysIYiaZK)k)3Y=j5<`u0-Y08no%{^UQL3 z>(^1qGYe*Q55UcZ<;zkE4TLScK%htI3kEibw14G|+A zh^aW{HX}AsiqB>-x?^!gJM9^O?waq&^x=1ewLki4l9*e9N=?nMy>bB2FLw#y^AI}X zL>_YER87XeetCnHkPvZc=*W+JDP`_$koV8yM-_1?rimLi+qq!^VB{@vJK>w-{x#bD zqNGAF=1Y40Fsxl?`ZExOq#eRRC=~fxI!8$fYvITpF%9J;AucLIk^8g&T>18*c~|>J zaOQlj4i* zNZ?{(1BzqhUoasmj@bYq1$+KLj>Zj3L{LomTXsTkCm>diaP(p1G#`6}3F;@D+kgNl zWPK1*i=aV-Lzt-M&AKAY1BD7wMF^BFt1w|t6ZE18Wi=ZG-*o=d{^WJ;%G5GNbwz1O z@!~F3(g!ac&(hR^GAL-mOnnk20jg~bk}9XeqOIo#NR*m*%?=qSc?#5Y2A=AFCKDq?(ie+!6JEpXcvT;K!*O@{lU*8!@l5Co_= z1)NSNJD}}B2j!dO2J%7ddAm}sqgl?+S=fwI2B#G|%ho zURP7o-`&XJN7W8-kjm;n1=ExYI>ZHGDp&Q)dke_YXhZD1gHmVe=UO62rcoq`0$Y?h zk{I|R>V$Tm2Pt3Ooo2j}eB84XN(I?u)iq;Q}!pcM??EL&gKAo+GvuW}E~4FkO_G zZ?t)~_EN@kzT`+0&rd1zXPg7f!GfbLz&qAxYp8eTqt_vBCjd$QLpj^6q>SM!7I>0Q zKJQOS(>h~p9ltRWgVK@jnz_xoUHB*p-}0SC1!*tG5>FNNwy?RcOu!r?J$>>@oF(d) z0CNQ<)BX}WHD6#}@fA{jdB?#tLET-UPLEn&Hc~RfNmT}KjLV^Gj;=N;vWwM0f$0bJJDC1>rh0J z<@L~NXv3+UILWDmbwPY@z~KYVr6Uf5s|H;mP-L^Xm+(FSiPE*v!pO;$lW|~@e~%$*Uf>`OL%Vts#!T}d9iRKa=y$)esQMw zrK>YJ}IR zFZ+ovV1as})}Q(iT=4@teF0K@=y$7F|8V$wcpNE~-$TUfPl~qDf0B_EHGbjJu+fW) z!e`^@CA@$EBmB!W%6}&+iyH0+?a!g%qj)*cXbX+==^_kegTj~cm4+Z>qhr(-ge|M` z7;C9qUC7CvgB3$-Z%U1YUY_@iQ6&vrj|lmn|IEy(lFtt-%)>g>b|X%Kk`@%xXFoH? zEd#hZN`HXLY;d})xt2q?q9>;{R;fsC>4NGM=x&Gh+%}Ffu7TxD>~m%UT%7hiYv!`P zFjo+2dU`_CrRW*DV+QFX(Wel7|KS^O^E)DuWSOK_sCa49*p@A4h-1_6w~2fRBS9 z&tM$^gB399^$NQeyUj&Wr!f#-JCBC?HBZ+V{_avc! z7sS4_Gui1`&0X*SE>(&dI+7x&k9ZF>UkoU4>kU2EmJ4wiqMd(=G zD_+RNM7I&I1KV=!eH%>MB44_PSoXM@qKck&I1nIJx*SJ8xlBmlyp)(mA6&$3ua|8U z6~M6tcpG+cicA{!x<6hBEk_iyj2(<1K^!_MLh{0o0Q-%?%6tC2mId>cENyg?N>xMs z5K%O5(bAdN=*jX;@ID+pk{spCF({-7tD^E_J-a>`pTBV!GeSw*tsF_YdrK90 z{?w3}I#{@-ZwKsUx5K`s2LCJG2o7;4-V{*KB}EPi4j@okErCu1_R(rCO>=Wg$pg!U zo_Y)U&;U7u`a1kG`|ikuwB$mf!x4rZesnyp2k|$AkITYP=8+Y9;<>K+4uuWE)fFqP z7?oZ0=GD=8&?|}4jpz#0u z!RG4b$@sr!2awb{hRrI8FGJY01bqoLQYAe;0~(^6ABOpl4ksbP1o-n9@78yAAbP5D zO%k!Fp#Kmb>6-JcGU^?)9n{oHPpHa_DfTy`qKAJfq}#A>tWrz z{}TMlxdb*{C3*0Yg4!#Y9=X6uqNjF2W%aG2$Xzyhst^9sSHKRvp%A6P&9Ls`(tW$} z>CRVR2j1~9?5RFH4=K(Dw&|vIgO<-PBkPF7IbqtW{GC?7`(fztB3H4NMt^#ROYV=|n|c=ksqq$h8MTX<%Vl^Xh9vvr9<*Y;S6tl*2y z_OQ875O@P!(Ca;LuD)-L^P4ZdwMJ^_wic_)q-X1+quN4Zk=V zuselnHyk7{4ge(A>}+C4oW!14&6UlSGx($aw6h3_|JaCRgzqJ#yS6jzG5{C#rwB=l zI|(kJ-LDwgp=s-^B!hCRtgMjLZgs{8wcx7lXW8Yf z-W~m3hT?jxUs=?6zL^~Qd!Mz*5I+e&lR}pbSaYmU$quX2rm8x_4o$nn>P0qim`}!f zHcBj1T&x6e7-3sbd$Bcc_#{Q$WYqOe9}d%0e=&d!MwoFF_%os`{cyK6%gFHnCm;tI z3|+$`sy^;736)=NHQCBavh}Lybh7qX7t!epku_#PHqz59X|s@O`s>!?^yT=Hrq=r3@iK{{Ju&FFq- zqL{f?%D>tQ>I)cN%r<1YmpxZtg-usm6>Jsfjdv!G+pXD7%Wg*eqP%OYJn2dJ=+#y! zeeIwfO#X}m_#WDGz4aWt zBEEy?38ul^@`juN%0qraWaJsc-4@;CTqs(`KFEiw8w zT?d<~lhg(2#VN+w^wEvh1nuJGt>(7j?9-XF@N^_g9>3S>r!6^fL(6TpqRjR#`dW^i zOD`TZY`S@`wLBFphJ+KRD?~(DL)PMSLFCCY4lT&${p$ucQqK+}a}v8`a2x`sbuXS` zHjZOz7-n)p_bY;K3Z94R_QBj^$_+5IYxofoJ6*fanpx|#^^Ck4xnKcc%)*PVn8i88 zJf^NffYVdW14bO(NQwujl)SfmKj@)F2vQNKgMIPM1k4amc(txbhjeP^_o5Or)_3>p zV9HHu!!Z*{)zdfkS*I0!nORfy>9|gpPQ@WG+gUE25bq8-vKY1Zk^4L6zqc-MkUj8F zlGBB``(p$FpR=wsE?v>>tV+IV^x|GCcXTK?t-pIZ3XTp9B&8p35Z+ z9}dSLCtN~RHvnxuF$tlIezw;t7=;4;s!GVM_6hHhy!k9H zWp9GwAZ*h*VmkT4h(3jH!D2LaMLwz%mh|u~VkSlV81dgic8g6_!5Yz&HGTcvaAV|^ z0KM)x%SbKn!*B0#p`N@zPDMl}QLCrQUOQtjY_c44Z{`k8RMXej-X@$Vumn_bWx>;8 zaABb<#V8~68DNwR{3h&4Dr#Z8*TEjqSd3T|I0<14;MNFwB+D4bT_LvL9iT!k?=-nQ z-Vg2xi}P-g`s5kD3mgHpoCKM?FdL6sHCYo!g_o@YM*K1sH9fo6Djc=yic}6$E#qFg zv8v^Ld-||7fm(JNzO#e@4nvsYb|gsD)JS6)AxVe2!v%G0G%t*U0Jj+BbmDKIm&Sg> zni#KRIUoV}3=4_lzimxU)sG427j0hGF5{l9U28TyAeS_LeyF`qE&=7VK32U&xKFh%Qz)o{6OIuLIM?E2M#v&AoS=4u*z z6N&-fUDo9`Goy6mX#@|r?JnyVMiwrqWOE!RVZNadOj6H1)~oyyM-)S7Ze2dY_t3NV zT33lD{mV9Z>3-|a2K{3l*v{t;T3ga1+NpVjraoxhY~+GJRxU4*)v$Gx7!4C0VKW+0 z%6tUWzv5!(qLuAkZ6f2hV}u|%KJ6tfgTtdYz&)}#E$CYgso=0i@{Cwu$VOD4wFZ%h z3W_k{$Pzx3q`^k)MV_Olms4~iF%0N5PfrXMYKj$VjxHeR5aJkoLPKvD+iH&vCzu>{$o~*;S=Uw)LH{d{PV}HIG>DQwCpD!#Y-Ny zGEP9~ROK%}Ze^XSJnji=nO~|4%R5?`6btEkF(3^`sTp_k!D&}%=o-tk+IGSQJzJ1c~&3-=lzGXLcT zYff%0OYx=WZ5zzPJ39L+Ipi(B?18g~o_HeXEDjQmRPue3`r-HjjRu@=z{PcKgfFga zykVdh?nS*U+ui~*EAT<6wWY5z^t%_VjT49d3sEE!D}7}m7|q}Z*5|P*;RZ(_;N;?2 zG^y6~4}ExTJl(&*juU^;9e=mr)=_K5Mx#HG1cv_df|2WEhqr2h1VbhNur`|XosX-BY{HQ~v5&8G@3mJ_P-SxKXxB_CVA4&C;Nm1g7$^^~KmkH|nrL1eu%`e@ZBRw1H5vDHUM zTUi->X4qx)SVdHf-hwu|z_zo`Q}b?f!+tddMQTXK&1z{#5=2G+d#VXGbP6Fz+B?}!P1T`$^bYB^aGF$A z^4u)S$^<`rp!(0OMaFEApKR}3J1t@wZ+&Lf=~!hGKDQRoqZLsOWXNrq>e&_{$@+4e zLO=N2dQ*4(l~1s!fNlFs#Cbhj5tSCc^Z(r;3JiN*x)8!B^_6%(8SnPdF2lY6XaA~U z7kJM;=^mqi#+z_C9u5FHyD$JCJPjUO^T_m!z@&i^BbAqdLmkhjkWL3pdzzCw(35bu z!?IV=Z{PD(Aey6{HdaFnTrBVe(ewSjEV?xx?_{@SN5P=L$P8hNo-ru>HV8%!N7)(V z^V``Vz-`EE{q|hCx895|(YI?Bb7ng6AZJws>W~4P@S7dvsNn>%4Rapg!rm{e3`-sh zj-p)q^e<7{y%8+GfH~GGI^egF{vCw98oq>*e&o-J4lB>}ma|k%2Z*6POQ5+=sImO% zdVKXNff8C&SD*%s)cR@h0#J0<`MR04+eO1>4gVL=D_ISE1-b$mv0xLT7 zs|0(iMO#gK>;##Xl&!nGc}vx?h|k!x(2^8;wlU7L1^V{rJhmltU5cHZ;ItEqSWR`Q zb_p#WV@Dz2P1qLl=1Ih~=F{?{2mH1)7o+ z>DoOczZ{lHL|U6`XH4~yYVyv@8r+?;W(825wkL9NTR+dUqsvd#fdJkWLKiPyL~2cp z8W+mTvhVhl$-$)JUTJmu&h6^BXrNsJ5}WM_h4|!>&>MMnk3o+F>|D-fre-r@Q_U)X z3KnjhSc0o}6cb^D>mmequ@Q3x4&`0r+!atQDsQm<)@XL<;4WzA752CoDX0*C-*<(5 zIkKUE4HX8ufePfjrP?d)oIxQ8=!YbMe{fKCwpq{<8)*7;pXHrE& zLIaS6G#0CIj!AbW`9PwvkglVj-V4|@RGx3&8?6Y| zAibV%XPxHpoFDlBC0ropCQKv1{X4RwLetD1z(;P4wn(ed$AgyfF#Y69)OlV2;YPNB1j{p@2JxWE5BnT&`=VmyzL8 z73=U(mBFTA0Z5%H>bN+|LS=9@+-N7?6P1$4a#g%Ey6+M?Hr*--U0rA&Orqc1h?vK} zE(KSz@hm%m?r8GmhAuhX{$W~ywETyoPgg&tp)7C}Mt~hUh+V2X$1aTZ)C+Ar$DVB_ z=m)5WgEE5FxM&G*!;6dUtKz&R)V&1Q9-k+KZdhVh+Og^yb1N=O3%%56m)dkrhM5{l zTxDlP=Q8Jyp~cN?3}=vwOF|_r_OzH`L*&83&`t>5&|%-7NsZ~?8~=TmT}wmz%*;^3 zW%e42&b`7e&2sM*UqIY%9rl7wUTvo&OCDO>zK-<+Z0kS|U18_=gP>G8gY-OpmpgZa z;Ck-WsH8Dh+JUmLD>ST90D+P?K5ly(ume4S28j`u7D=V9w(IET zD{*}HUTGJOTt4xixYdvugxzY9<8Jg@4TZtGh#;F15nq}^VN#&jPOFhLMMq~R{Ej+j zvF{FG34rJ^T)a54F~^cXL{)BY0?4rN+QA?Lpz=6Fkab~phFB2&*r4vs*rpC(B96T( zNDb^;hfLv8F2Y`Thp!A4@V-Kkioh3~o*aSFfT@%f0XIjCH|L$xc4D;Yw|IE}dbOQw z^NQ%;mG*RCuHkdy&Jbr!<4r&USm1W-=?)Pao<4sb@A2JN+CQK-FSpMM-Eo!utnK0( z2Ule|=cL zYs8NgL>Q0&$aG}sAOi|`)&}@t9|1X62izE;1K~474r2H=SKS$HXUjgJ>BT$bI$3j6 zUblUU8rfS*q+_EqKeQ9$xJQgCywPRPV$el02pH^NQHVOib9d;(k#zd`!_ZJ1co9y% zk3L|;i-c`Yy^&1knZCH?-bH#{U%lTwiw8NVbcZY;cGQL}$nWat8GvtKcQ1lX;^|^b z7kWL%ipl1h9%BRklOGcNm*6lB6XVNlo8I`)it)=0rRtv;DHk{$wl4(rSR90%w7aeR zeyDAboA0%!ov023b?|U@tN+La@vBB}Wce~On?#{tO}UmV#!aK@sbD2}M>o3T$&>Od zB64=U?OR~H={9gD=-T;4%!{WYVxrOOS-#9zH6P|-8axfPVNJ+q#*oax!Z|+ypAu?dMm#I9?J(%4X;Wb_&)YK?Htd(& zp|CdYR7cz-Ou^YB9k13iMNeV~ab$cq=*j#186#~GUk)CQW1zdRBd*XW8#F>q?^elZ z>K?jC^d+{6PMhjOWNY4{$UEu12ayLUL1_8Fx+O3Y9X8hycgQox;X8#Hjzz$CHAMke zELe@)9nP}6sk;rE0``L?08rF)uEPT)pq;+~40!Ywa27BKM)F4ARPL@i^ftF&m?6cE-BG)NZ`+VaTF2VkpCEb3si&ecILBh5s+W84UgE&()>y65KXd;u`&K$}yBjJg-7BQS`=o zGp$e=COXH`&DIZcbJzu(7eO=b1a4q;z0F+>ed{k}$qxSp21rhcdLA?~$IJEOg1b9} zUStgnEyiqjHcSn`d#D&su^qyXM8iG`6_2l%#)rOs6I8;Qkd4?xC|#yjRk2%HIqg4b zC#KJmd&U@5{uHmu+rYOwKzCy>Eec^%jYz<%&MO? ze@+XsA+nyDY1;fE&R7Ep%=np6g@MKO3zwpZ7bFUG`}&54+4U>xWiHS`M8J@LGu!8M zJ%8!qIRT8;0Tz*6qXS!8dpp>x!S#D+?-vRL7N4=_7&C|*UUz+WFDLx$+#zi;#EdX` zCbxTwBd1VOJ{4x9&A6GYFJtz7(k#`V!!1WnpQq-`UCi_U>clZ^M3>PM7lQ+I#dM2! zzLk0d1WBoKt~kf0&Z0HqESz%Bu`XIVtA3F_8!f1V1eFh;_$6(aFVo! zVW4(OoXvN>VEWFUqpFJ%SYRt*O&lAn+}-~8t%5Ezh7Voxy6SQ>$nh;ieAjU$n=czlYvSB0VUI-Ct*MY8J2X)d5$*5KPEE|#vu zebfGEGxdD^1xQkLu$UuQaRog!9nVc-M~Hq{K^bZu;KE>2sBf0YC- z40&nnjs!`++hfJM;OVVyN=b+ba%8$r{7--%&A7^!lL8r=05_G& zi=l}N%KgF_co*&YOVoI(yw{EjjVF7d9j!yPgnGVd|H@pYEGYWBH@D01LSEqDaJIM` z72TXHO}?@T~)n)$y(LWw3 z%t5?ckcP@GAv)pXMCXo7A%N^-y@-$rH|rW9UJ%V7S5R*nTl7H>Q;!+rZB$tvVT&)O zh=UZ>%L8e_?Q&T%khN^QD>Uym`)dYW^9?AxU%lNfrRe?kf5aiLB@CQ^e{p;0hadZb z&buOpnzI!H!5352!C203+$>;bnpW;BI4j~iZlQdf^>jrBrFqg{dJhs2)q3X z(+NCO0rNV%^RR{xmz}^1Ww&=v&b|xj? zZBLB+)^N976#4C1)Jt;O@(+IJWFOCtT2?ngld5~{kv{j_gV~hk!DHn5XAOUzGE!eG zU7m|)i7D%;m(zi}pjHDsZ-J9aRJLym?*u(~yInZQZG|#`WCfr;Y~&z zH9)V7YPPjqkAyx^74_i!=kb6JYr^rTbbE#zg(uJmlH^DH*#sLA|4{4uY4r3xcKMKT zd!G0b!tH*`fFKwTB4gOzEkn0IpGmiW#aB&R@3)uJo@}Hh`t$wvx9kDrQlPSrqq6<5 z-{ap0ZuMm$z+v@4Bb{!4!2WKQdVir_Wnj^082~`XHp9ne{WpxHu?!A!-CSqr==ai7 z)l|JUfhBZlIvjo0{25*}%pB74gZ4a{J3lH&%_r>WJjwsf)+y*4Hi2^jh{=TX@I*dn z|CymhhMlIbJZLYV*}VuJ_2eYOH(|79tQLk?Ql15`bbPez5IR#{H(mcxRBSw-L~n<# zg+0jjp%tp|b_~|k%$mPo(d?GF3mTWN;B#%H9yw& zy2CbOT(o1M{3SPox<8JJ>*7t|wdV7h9BNTn1@j$I&$0Xx_9BbxG3o}Yneq-X{I1K=o98k9qH(9AzGR2;TgPGVo z>C2UR2nz6%hwN%PG!xQ12dd-4&V<$;g8l!{L3@&kSKvUuky^Q(E3lh3{(sV(z@!C% zNz1@ujU1dEs-khQt2ohT_)KJrKHPwG9QzKzG%I!%k~8jp(9WE~8zPmWxc?lMQXp@& zh%mU|LzLH4SZjFV|G+b1mY zXGLY^33Kiq1}23J!=Rt4Z!{*B7qEjSVqAB__l9*ELex@Oz~C2#Ke^WbVeif3t17Sk z;mf5R6V}>l ztqN_uN9%}FtD=S4tJWFstsSh29jvX>`(5jK_CEV0LGA6mzt8>Sr4`QJ`{XK@O`_LHYBCT(zSMd@iE%hr`t!4a>#oSpOdv8uQl{}VS5 znH7$Q;v58`oVL|V#QQ-PGWm!GoF641uLJ>8H57p?i=h?ewEw3@`k*xG8lsK`HfoDH z6j|K#k+poE<6*=1>4 zpCIp{!{?_Bs-6n$5EHW2V$R~U1AAfK4D3M!sjH6cbgHVKNNj59SS=-2Q$}L)5(_Ca zlg63fSp?n!qhufgZDCUZYCX$b!-h{-xwwQObdHk~Q=BJdWMWr3c^JSsk3t^)k6)FA z47=E+3O6JBX}9JgVTxp3q-qJS0S8yh8Ii-}S_dvBw@P0>6}6dZH1`N{W1je-WzvbC z8Y}4F13)tmo`RV2_Y?tnKDY#7Z>l-3;%!fR^60*!-uT$XodZj@ZA4g_`gz_mN_>MW z6eAGt)hm5@m1#=)^F#D0&z6V+6Z7iaBDq)^AcWW1+cgf1&o1%3O>9g1DQEUIP{IYFLM9|z0m{`Wk& z6)Y1Ig1+or2z;k}mxp`S{|7PIR_+pN%hQE>62dR3G?(iBgJh5d;N{eHM&Q%@HsF)5T zu!_oJI&UX}5)|Wfj+Y=bJcZ7lZ3=;@+`@(OASZax4F>L$X73kLK(Ua7Qzwl)#*j1= zD!6QNq&bRYLUpK)l)!J8Oo!7zsWIa)4o1~q8#(Nv8XYkRTwEjCUnpIPngs;AFuVGd z*6v654=m;yatvEF-JcFU%YoxY>Zt*pa+yQq0bfTUh?#y)@5E9eq%G_3-44jy2oE|O z#IgV#d)}Bvv+seW>%$Mg=k`Q4a_w%q$LONVE=myCHBpzlsCKOjwQlOb;e-~AeIRg0yG?6~08eYzSCu!1 zKx`GST|d7$gs2G0)3&g33jXl<1-Nl_tiRnQE!*jhpIx`3qiq|{2r@9$0q7XWZ??Dd zrUe(CRm&msit)3o&@~K3s^L5DHP*&Z(u2mn7@!%t=|N*^vSZ57E1A(lbm$Lon3L=7 z0`ldni|zC^zIdcSs<{UFHA<(#WoE`fKn-QPt%vvP9V@oSuYOE!bA3%iEcihSuub{J3 zA3LS`(FLISp+h+cl%bz-CaWPp165WnRM}*uHB+}ri?lEo&Fh6+YaayNG&@w{D!~n_ z<}~~}Zz~i>15DpH4nWP*jdR5ori+GJCR+61!^YS+XCU;{!^Tf13L796u8f^f50|m# z67e92n7;9du|UNnxc3obLN4x&fYK?$1($7OmqQDL=z#t*dhros^7tv$%>X!MB8Ii? z4sGUf;Xk>|=!!>;nKbaQHJ+N5c?;s;ho>F3o_-9*hI1b?;>G}7Vwy_?`-h0%=gHI* z!xY^q`|;&IYJ?U62>8Zsks-Bg!D;D2csbPLgQ`7Opj+ks^cezV_KP`%3(3;Uj~WY; ziq_7Uxvsgrxn1!(^oz%gj0D)L^K@9sOY<=%J$qg^db>6zE8O{*G32#`#1GQE$BnWj zoY6^P1-AXjbOfM|% zqPODQlW)ZcJhPrh&;n1278^%57nPlKt0Ewv^LEqN$BiI-Ek|2pn)bM{fa;$#Ceq%= zjm+g`2r7?>yJ16bpCa?ria4w9-VGb(965xao_WeBOBs1UKYj|R`&Y|6$zw4nTc!a3 zrlco~0v=2$o%Mu~l7uaDz#F5WC@ZWX0^|#=$O8#edDIKS*K3l)Vguw ztW&1Xn03l29kXU`+!SO)sH8@W4e6nyMvuYqX{quUysGpFzy?##F(U`^XcnW>?O(F8 zC^pAT&DUsf)G5rSy~m7fy<4{(Gs;INoY0vZScStC$0b4lhqPjhF^rH>YtQ5_rzlmt z1MImOs7`{J0UYBBRRze@`-LoLzdEhEr#~Gtmd=gL7t}nnh8V*agetD$q#?+&fm6ke z0Jrm6U^ODRjmAC?XBdUw9A&1wq~*s9f7VmDYRjL z1={L2)gLp)Co0K=ezWVB#_9CPFA;bs@p&JDb>(`K>5uOlv2imsxfY}C!Cb>Vu7yrsA z_6}kj`g?cm3@WcW{<(V9GO26lUV^kEB$T`!Qp1vl7L;jegbB{_I;9kb+7@ZnnQ5tl zY!fK(yhWffR`++-^_-6lbZkhPni^UfR;^fbcFU@|m8PS}girnKsWwK>^|Pkh@xMG**Hn4>+i)X&+EjPbiD!++Xw6&RB-;C&vDl|+ zQ@Z>)V?q2q8wbH>N7tsRD#%=y0I-$_NwcW4qYJs+`M`>Q9)0_HV~!W1*XsMsiGw2w>XT;+ zhYFn2ZGRBNqYfj$boep1OYvN{!RxFar&1MmTX`0Ch}A3NL`kq|VN!(DDGKgduPKI7 zwyjjcUoKsZ)CX0F4SC!YV}6;2GP-p#gm+^O zqqF7bA_CaaHNLo5WgrCM-JXHi^r3_QUN>Zh=h3Cxy}qFp;n2{ra42ZshW2&~MZnao z7Q5=3b?Aq86-*HVLcdq6ye4^>ytF4SEYHx12eT7#A8AXAQ1MXe-6dm{0foJ9Lzms1 zI~c*S;Oja!0gBhcb)^jk79BXTZc`rpmN!{$DquRS0X+WucS6d+_BXB)nm-t&O=Z4} z@iO%8_QVg(xqDVUqs>TTVn;JdRmM1)p;OYxlav|izx%$GWx@C|um?*wiKkhpchdmY z0-hfs4q1q(HKppl7s;DAU9gV1Ip7*dGe~6i7m>v%3k+5K^t=US{zZ`PMYZ5}+Gg<^ zyKity?uMe;`SA@uJ>nVay?6G|p?iNc3=)Uh?<@Brp;67Hv1v=4y~A0OS=#}(?LEcz ziqB8K2usdt?eAgHo@iu8z(RZk7Q#oLH~ySSAH3#C3qSu3I0XsC?y?)*`X?h;^m$1q z`qrO}@B8#&puLZoh>LMPXY@Vv7_zu6+zNK2>CeVvF+)~cdK#Fi;RYyF_kj+(e()Js zW{-cu%osA-a>hiJqr-oJZt|{Y%%mZ2TQ)82iAji$Sf5bO2MGC`^Ei&*o(~{9uYbav zGIaDzYizWY3f<5K$DD>I%=9a-j!lL0MLNy@n=xgjGBWZPuBuzOdR1dx%K{isp3xX_ zpRm85-ngQ^<;P4fvF^#e9K$QrKoyLPXe zazXR1&+}UqZr)uy#r4v=T-io+T-~GFhI;c<&G-={mEEZ&F z8wSZ7gFx#JK?)!!GB4t;^+kx3+`Nm!C0AEDYvS4tEQkuH9Q{#1OukLU_u?tl#lf=p zU{P>`3aNYEHi&|YPKQW&5g6&JDlJzQpNC?|*mRr4kiWUNO1D}JUb}dA2;0Coyufw6 z7WPxkKOlhIQsWzow3kNwJWekKhdCg2xq#uXJ{_!ri@s1!_qMl%UKbU}1H|&z{$VUc z9C-5sV@5D~EH>E46%LNNEyP*HIHXnnheqD|!d;cSSzr-aT(+2l#0tj7#fI@+3`_>dgB9Q zkEMF2O&@}V362Vn2wVRNM7iZ38s)`GvfzNK&7X4sIM~1o=&B2lZg}5FFSpkhL9%UA zQ>->}CF4nLg6s8+b=2^YF_{`Vya~hH+=f!lOe<<{*M2HRqEZlFw@#=UuyV}q;U6q4 zh#6`vgex;!JHUa=x%>FFk16>#AN%iiq23}pRPf` zCx-)h<Uhq?cyb@B|@9DNAGk0*;zt~O09n0xOXaCXguXhEbkgdkQJXf2r z9dve?=}_Kh{!)klglD$m!th63G4{!w29d13UUbQ|b~CoC9xKvRhrFt)IoW=jVbN_> zaegYCcODaB{Yi$5B73Bgdfc9t;N#t{b*0@O7*jUbt7)&YtTE$AdqhG!I}?*Nz>xIy z=72a1q}55VVbpGylOs(^9F8KF$~gcuh|E7;%+W^ZSXvEyEOCoM>i^V$^Vp<& z^btcE`{oBmZMlZm&l;gU9{T7*W72fzXaZBX?y4YPQ&A_Ua5`XIE-9M#5p)ZTTC*FK z&tMVuEDAx5KuiJp>TPh6JYaY&I&F1KV)?{TkpN?Y-4g|yt2G8*RO<0Pw;34={xwwc zzk*4Uon6$X-GVh|{|i`?N>|6^41NkOQnCSi8wLIqWf!3mTHr|uk;11mf|VXYxY<+` zuvyW`(b-0D(aW_D33MfU10FVHQ{1#1avRlvb`65dts{;y~5xN#8AQi?QY|)gC(-cmsEEC^=PCHW}M&U<}el7A%gUOG$9i!J3dw=pvWYpU+ILM@hz^jn6|Kwssbr0ai z&4jbTY@PdJR6WCv8>|6fw^SQo%B5P~;}9D9QjKoYMO8CvvzzhuD)9kil*)%qZHRpd zYh0_~;(v?PO9HRwrn2b4kB!VCb$pl*jOL|mjAZk5ba+B+4lVhIky(bgJB&RvPdk$4 zi{2Qa`sh3F8)@vt)12$^%>Vu*pXm!v9%~N9()@46jLBOl3{|uG zN0~S|p+e|8ndX_NLP}ZM*(2GsOW71T%Hj1rJQQJ%h^rH9yh z((2qBu3g54?Y^CveA`0$=i&g6rt$Cq{ZH-w_dmLwM}k47Lq1j)Nwo`E>jfZ$=&p>F}@a;i$t(H+<;J zOi^8^H#i9Ax6*<4Jn7l)*F0{VVY`$%r3%YA7se<(;-$vpbi*A+U=s{iZHo-iL5o^M*d>4O zrf9%-#^v8NO2fMY!lywPqRi**Zp6)kURec^PC_Z9%hS}Js%dY=8mS6X5Pi8~+6*iR zOqE&D-F@;;TStDf4O3WdVl&Rl%6`}a#{qlD5w4;YfAQv=DN%FeLe$khWv?P_k5)({ z3Eud1^!q&X3_t=;#6J7>(O27Ai0Bu9O_Vsf{YXFxw}S#iK_P1WY>){*^&D&IvBeq?drfiUGY6bq8CPQ&M|K9 z*whL$uPQq91U%f{{=^$=3jX3OYR>@A7%WHoBr^==k4mbg4XvFcje!(Ul#Jsl**J%IU%mrVs)3 zLexQ|jF`-GTDQAh|Nk4~wPRJeW~Ek<-n|NzY#S!{O!|7h8KOHpF_{@I=uz?;1_$a} z;7boDjW;KFQk>flN$Mm^C+%J7E2VP_5$R^(udvL)T-e<1onV&H9rL}u6kUvO1oe|| zEZk=ka8tfA!MvQV-0wx|CCg_R+#c<@(VOnml~nG!>&KgM;cNWnDlgp@Fq0-BwP?-i zrWMOp)YP`ru3p}NrlGR#C}nzxb+D!F+q(OxB4B0~jw}cFhv_2I6U-lM_3hzBU<6ygGgR($$%^OCsr-%+Ojmayp0{|2i zc5rNfdQlOG_7w(;{D$4jGqd%B0GGVR60k!!{XS@x=F0)rcSU<|t&S<6o0Qa%^xk9> zX4m#AV`7DLTF^YhS@%6b;qxm21w=@Lm~>Oxg|D>eo|z9mCoM@N7TL-%s6T(HXAeNlxs!$Jhqks ze2TLJ{iGP{6)E~*vAN=`(TmEYm1B&adWTrScgX6w#{>0NNG&X^JvgG5dZ4~3<}-P2 zEmnmfNvzwBPWUB3m|yuJkc|IIPy7sLPLVATz5mh`?7ElC&Qz>VIP&}PE1-pOK4`6w}zQp`P&>=GHAQM1=FguJ^-xn3rx~XPE z+``tL-X3_|bkS{7&F}KL(27S|S!!+@T6Jgc{Lz6S;sPKyr(9lO=YVM=5QFQ*k9+i$ zD-dGf=S46#Sg@uf{8p*?H!uBZCQj@7tIa}BICh%(``9^hUln$S&H@M6yB*Pnp(N`Xaa*Xr9KXx?>J$C7{wUZjY3m`lrbf(y0h0+|gj(ZP6=@rbTC-Wv-#`oNeaj zHYjyFXMg5}h=gLEO}Cw8{)4)vnUjibGQ+vvPF9N!O*5y^V^d(I)$q1qQuU2q)8Mx0 z#CDv5`E4G9zW8UuqN$D8pf}f;D`|QdX*_qGZT`C_!PRQ`)V1a;BZGl}c?c)a0IO@+ zk!l=GT5tN}%QixZ1|E6GPRdzlPM+WDkG%13hj~$J7w+};o$w6;U4z)SI{mwj|24Ad zch)1zLc@G`03B#C^XZ-zbI({A1b9qXorA=pIUCHp7=R&Sv&Z48$0mv~YMXuU$A-IP9p7;LvwVoWB|3goFIDeygz!S%P2FrjQ zDX+%-czT592+XfrxMYQY#iB)G_FEPoXsm1UFIZaN(p0l*ab45gNndR5t7vWxQ1vEr zqQPf?4lXqF&g^^(e(8zrKNf8VeE-~k; z#KT8_=ou%8YEI0EDV4M}NP-K`-`6IZC?-BtTobb)HVlhFjuWFPm{b;T=FtU)JTF->b^QKUXw{E)Q0>1FN{8@-RXA ziFC{Dz3PmZgT}E_BRsXG&@6E}1yj$)C6Y^EuHRXJIAtmnBnOZcLE)DB}R9Q`vN|7MFB;Qo?`SX72J*!*qn#ylOB0Vmo1uu~}@FTS9Z+ zLZ*%6on{wZH7#~R2ti_BgGtzkP&=Is%Aj7>>5a{!Yd;sMeVmO@u*Uc z7Pz9nS^nPO{8G>IcT2f{i!RVo9*;U`nE&S5uK%fL{9VL76mxg@8sE(0D_4K>3;kc2 z>#hA0-za5@m0{us{DoksjZ2;-rq5qw_78%bl{-!{EwhjI4oGD~SW+xZi)Qp&7|g$b z^yfg1xb~RLvNdU+Q68JAuE~=%H*uh7{^;LORYz8$`z|ua(Jyy`j%xji`K^iAqD6>s zfbivZipUzr!&IX0g^I%Ge$^x|J^eMag#Ph0bIqVO4X*Mxai{>WRUKWueG-jz<2FPL zWceSwc7bOQ1px&REW6)7y~5w#x)Tpw?PdO@1Nu=o2 z5nI{%*dIWsAT~QJRbK;KkiPYGvu>mStbRqy()y;lRW%En;II{>>r;ItbTG{~j{g32 zvux15cMu{E;SF*9i4!d1Qy?@uV77mt3Hzcj2Jaz<-94+fe`~D@8|e^&_3AaM;R|1E zU~S~;;_!`JL4CopV$I>M%$D?RTiQFvpm3x~D!ka7GbO5qG{PlOF!}>1pm5{GrpH4+ z&o`5k(QecrsPf;;%zP0DA=X2re>FDY>8LN&m$OKJ6{QD`cPU6i3@$?K@%3HkY66hy zQ0I}?bgbVB(A?8Ku`~5cKgO&Ajrl{6`oRm3(?+PJvFDaCz89^0TD}+w*dMsW?6GhaLbgXKmzk5orI(sdc_{BPGuNjB+sXUb+RoO^JvbZk z6t`+yj}kM4-?_|0()wpEH>afP)pG6B(1DNrvzKm)Ebvq@LF4oon-QI)$F&EbNJeZ- z8Lhg){58F~$1LLT&f`vjELD4=EE(wTQXD#nCP?@ZToSg0xUJhmnsayF!|&w0w-Ji( z4=1eTK`gkY7jU$p)e3Gz4l2Z5kDp#CkI?n;!!&t-Cp#iH;1TYfD7v$QV`K313|*B) z(DBn}%2PLdt`YZ=U(AvhZ97|~@+tBpiMxZ;^EOug)witN!5Pv0&v5rYBf9??!}^~Q z)&Gp){m+Q%e}=n%mZ2&+BJFtFDuh{!tN~6g#;sc2#;JG_m!qHOreYMcX1WEUx>+Au z>9qSiD}9U-6$1ky&}m3aXrXvcpA7M=?1cnY&@iXo!SYMFrDFgh)FcQs<4;Gd^or@z zo7aktXEt;^-RuQjtX@`h?+yiQ>9-(4oPX6EZ@*YUJ3R`C)D`*4re+)fG$ben1R5XI zIf(e#gS#H1o*RJmH;OB9vSXGy8+3zR6RI2?m?%kRgR_{zb?&td&dtWciCmw8G)-HzQAIPdj1;x*Fut+~=n>c;0(f zMkbs7tAW`rmojAjHmJT~K1yM;k#`?@DSHB&K)RN0@8FoVRs4-duJuj2h6_Yo^_bo* zWv2tAvql))Dk%k?xWm-|3(lrQ7+=*V+%Y)8z1}Yve3u31l=Du_qt=&vS>mJevDZqV zS$oYJ6)(y3+x~qJsC>hM{1?IAt6>6U<}!Y8IpL(!8=O*Pq;CyfR0DI3+514*9y=Wj zX~#bE68h0I=7fnw3ayExE>u<3FkIx#;QP)#bK}O1S`xz;T-2u$Z>n@&bgVo3Hbu2U z;5%e2X6enR*8xa&u_Xe%bF+~*pKHQKVKHJKw{|U6h6=h3+wBy51JJjQ6*JtPf%XlV zEi~_&=9a}QN}UPJ!nSp6zQNh|ih?c7;J36ey^I@?nJAE%iFWUs+1}Hsn5YzimDf8ALeyYI7?@l-HUAlF>zl z02{s~wD%4(e>|qjE|VO-6ke0gJS1RrVLMgidvjUYZ%6*2?_Out8eE#*zs~$m%0FNR ze7s3mO?vt}Wpo6Eo;;pINjGi35>+ zp6wtRxzmL|#U>}-gcGvtMly#ns<_F#Y!F*J8gOb)ahTyM_rhhIl#64F zZ9=%G9>AA?92R>a>jc!-G6;ql2obRu6Ib$$VfFOeS_$mURR@RF-Cn|#0p5tUg1{Pm zI}yXBMLpWFR#lT2x4Q>a+9ckL(WP9khn3bgjN{$Y8`&nonXdpErt_XL)5b(;#FW=b z=J(7m(e2+evmh%uQ37EsL@Fsm2ZjuK`+H^u<$vECpk*v4y5akf?j1%JQlv(44w77K zu4hU7ZA-9JN)Iiu!53ar1`{Viyeh5P3;v>yC*C{4<&pJrH+SHriZ>A5HFDeC7LzrI zuhGX{wE>+#s2>E6Ek_gE3tPDn;viz~P5}idUdlW?ZiUE%?E}%xDb7c2YEZW|e5zxjJjfvnNI&!p27wV{)7*O#p_kaRH{wxtG%2c2gJy?42 zpt+c?>w$*&f*+aRqlMSSPNMoBoA+d47^7h^iu(zW@>4%D^Gw+kT6rCmsHoxdLyHFa z4pbUZ(xYbWiHtk)t)D>lcu%qwXDNh38?H99Ynb$N0s}TT+XtYznUR^sWe_BSqit3< z2jgk($o@k&{KTA0ZHZO}{plxW<#=w84f8}{s(3zo5U5Hu307K?j(K3er=z#UX3&@K zGUuJz1+?t$88S6R*lYEb-D`Bia3cxaY0jl5lc24?{w_0+KB65!efZ2f&G$VqkQUL( zu=)2mXMyS8?=&aIHPkdM3G&p)Yg)m?_@(s=m#wa?sC8J;vIbe<=RAC~Hksz2e!G%ch0=)CD?=i>HMZm&{+MjthT||ZO6ZY;=*Ue^5KFihY&Xvc3 zd)Vjy(A+RKx=(ub2QU~&B8W4$-eMM(sqx8h1dePR;%-->6iB;N^GBTA7uJ|#Q@Cdy zI_u$B&QwIdxW&vwOlOQaa_?eoqKL=l<~g8=LLGh0&GY#3UfYa6OH4)rh~ z8r|RrV>oRC{^^TT5XbVQ&$l{=h{LPHL#3zInB!_kV%3p-+W1`}$2fca|L;kk(%VMv zY1dgu>~I~1&ji}x+mbcPwlLzPL${b|V`U5NLlvR}x0uaMa6JnailtGPTd_)l|tt2S_z#BSxw3Pp|<1v$TubYVXeN3eXdXr zkAUCFk~Rqz;zebE0{)bU39>Fg`tniR|3-L8Xa^%LMHf}gxF@Jej$P-Ut109wmF_qypB`kEIj55bn0R# zVj%pagAGXBaxgB&9Mm8bDX*YaX`dM=;s#_= zTa&sG^j?3DIj%_10?*wg$5UoN>Wgz3?7qibXsLJf;F#DYet)UmRaQoBcLu;mwM zfzG}b5rzxeR0~b#Jq)q9?aoslgIkksxX+xNz!$xA2;csv?lX%Fd?}>MJ~py926xSw zGp9L-uom0l8i0SRtJr$x0{(wLy9cl!Ir?RDb7fKW+(@YlW^)h_%z%k7{Ob%Bdu;bJ z2=Can>jO;JteaBA|CBk8#Zv+oGzSs$1vG~Hv;B-{G{H~lfZ@&d=3}xD>H`>y!0vpS zjxaX|!D$T!Dw**l1XC$1sh-nZt=h!b=po}&=*7XHo2%!Ql{jUq=Zz?hvN)eO?bs4+ z{^yJ#SS{9|Jiv&Jj1&izIKpmlH>J4bo3+LR)-66iv7)@5!L;)8Tana9c~zr`qQeELZcmM(iALh#wYGlr|1VBVWL$=~n){?lrMWKR-=2&k8*qjaW0-oAjh<}!p zV5jN&B{3O;!5%iwDeTm$5od)Iv^lb8%?$W4#z6A`R6PiLDS!6_7UNd7d!%$HSdZ3$Ne?l0B4^ZXL%#2hHPudOtp&HG4cJ#~p@Sh(u zQ;cLSoa^pw+qw+#_>qe|6LHVqA0LyFqb^|9&ar*?D%f9P+ECQ{>B5_gsdfV6cW;Jm z@r>^o1>-bfq00*C(x7d@sBb51QrRH9Yr#T4+n==|qCY3nIG;@!aOtqE1XRcp?}>9P z_`5sdod=@_{A5)w_sg*#p-+eY=X}2edq&-|XoGQwER!Kq!>x$klk^Ov%;USwOoN9; zdv7(;=(`?kTsFkf%7_ga#?%Cry$`+ndd#>?<`7wSZd+a|I|s7o>z}}%wT#xOjw@HD zfVyNpY1WI?s$OH)#*BZ`oRq;^RHUUK!Z=ELr9)3b9KQ8Qb2~x^LezsTQ7cb-{)&b6 zKL;j!{x9&?y-%BoFk?Laqbj zuZT4uTGti?lE8opPMG;)qs!3cXoar(g;|KG+(NzruAnp63@1)j>f!9m+aQRrq09d| zi-pz64$+~j47GkMI~8Zr7IpZi$!^iHS+L?yI%;mF9Y@XObDae&o!1XjoK`V9!4;-p z+6GVbZ-+3`i^OKM1(fov=`Up-P4i9Q)A$z06m1a*tb9u6*>!MlNtGH!()Ger6RBD4 zB}5(*z;lDV6%MJdx0jui^a|Ubdtp#Fo7O!C;n6G4nz`%vzSTibhZklp0!s$+1}!;T zr6CZ{?mfl6*v*vAQx-!ta6-C)WBF%43ngdVR1nr5yae0YDaTn-)!WDSHb~zt*eD@v zHvcO@Zn;ae7CC8Ll5IVE`*_}bpW~+-|I>RnHUW(nAt)0B9zo~4ftCX{#j@EOpr~?P zi(l4&(B?%_DlCXU9Pm6(hNYkq%3#j<*%0~2_<_x>V3vx_O9P4#l%Wk`>{e=p1LKCM z?3kII_gU@u#}s4hH4xxrKYY}js$$fEGT*eRD02E_+K%6ZJw4`XRXP%Qth%NR(@ysz zp6n4~+#%Pa9x&e62Q!}b9W!5`M}KLaLLdFoJT-F6N5S{EJY}9i7d~TdNp_$FqhMq} z8{Rc@hhF?Y^F zZWb|`oe^Ik$b=JDYT&Iw#_v9Ab6B)gC0C)W>_$yjGsK2#y$fZO1^B1 z9n{O1NZ*R_a`08SIcFfS6rP-HPnq#F50eU@iKH*SVV+_N4xnYvo2Sk}ga}lgD zdq{fX+BziHfCn+hSYV2xKSC-L!_tj?c<9#W&B9zkPp)b*AQrWR#Qj}xA%gVTScBeu z4m)%EOXipq-Y?sMOoTIZ;8#G>Ilsm|ynD!hV1mM)6|W!|>xaJqwYK3EC{!OfYMPR3 z<>FV&vdJ!ZbdMcgWiaA!u!R6QC;Ynf71))g95ctopzrXLubAKPcnavu*Gzwr7Nv_Q zXxMt%?Z7Z#TR+|K56JG1-xO0wZ$AfbpW9zES7$g98--rgv>0M@Xwj8OWA^f|&6Iy_ ztutOQPo@5y@aRgz#Wru4gV8@#H_?vq}YTser(vUO-VBOmDqp zuAqmP!y@RDV<6mrusva;dzAkyFwZGx!HY1^>c;)w1L->3J{Z1LZLqJyHDE)S>*C`> zuBebcp>Xi*z6bC)9M9vGCchUUujeuV1vn>A6>S}*avoWq!WT`5N{@qoYCmp%$W%#( zvix#U0srsDm8D=uLc&Ex0$Z5G-nn{poGHL#9dN}Z@0jD$MiG9%2LmVb_wOL;va!>fLGdSmlWKQ*Q$0bNe`iekn5g_y%BAwg z6TnTQSIIkJ*3OF}U_s$+gxReqL)0Wxi)VK9@5F!n4Ky4^W>euZA{gdq=34I~c8 zAqZqNriWCQx%shm$W^9N}pJJD1GQ}M|fLY{P6*56{ z8A1b(Z4iM#qq@V3#fCLInx1H2PDIWxRs7t{9@CHI(aV4Uv$cQLVZ(2E&OF25Z*=H2 z03;A&z9~iN-N(IxA2ZeGS6?#&vGFj`*a1@@diu|1PGZ#k_QDafA{U|8bh@rZQcjTw zLX;mr0;lCi+Po=)x{b&;fjB!nyXruH#DP0WllJigG6AkQzmvTx0Ftg*s{2=c15x8} zHx_sqbh7_N5H+vg6f;rXq1U`&W`gy;q9Ml57Xu8dDsIM2_u_y#W|DXcxXI{v<|MVw z#_kxc{0KL?C5vkF3RaXD=&APC;Zql{C;I6+j?4!#W0-U_q$ zoaIJ_G~Xn7>oX_EFo`mj`p0kp{8f4*Q1nq7AQ`*)9{(J+FdT-}ky%96s^7 zneIu6Ft_^FqT@fu4&R%L%m2+xPr_WSwiDJxpA^PMG9Xn&zm*j59p@|bsqa8OJ{k1O z|1wkAb14-PMTO3cn{;fGZKWeCQ@Uh)C zy-Sb%#_VUpdf6L@-X@AaXtn9_H_T-a1_~{03v`uQ5b|eA|E*awc(SjK{E0;s@s1=} z*Jdmu4a&h@D#9le(|51#N|wO>jS7l>;(hCa>ySd;^!9oq(XR+B4aY!tx{|tTf&Hoa zM==vC^;;<9G5yhxpMoywn5&)lKAff(Kk7-!!IK*{h5?zupFa!2hBfhdz-w3)2kw*v zcC;x>?$BOuh=m6f!tP^w-27M+UnCx74FY4d_qXQKcm+{hPM`tecFD8Wq2|AsV3c?%TMRSNzVrKLZtPJRCthLo0uu8UFga=8axDH^w+K{PJJS z91mq4@FmlGzk$&K3(iDSxeU@^Hk@TIBT!5nV7a%mosSLF4hT8h(SFtzSP={>Hr!H* zGlvEdUn0sjYLpImX?c2;?r8pjUF!pgPkyrA$^wPEI|C5Brg=deAqH|f^%MdtjvZ?x zSAL2lPQ}%inM5we^HikA(pSyY7G7ou$2YeK=oU3N;yhcn1RsfIHBF0FtXkgEP_yui zn#FZU!?n0>4bA|AvYf6akr_h@F}$M?eSE}wT86VXt}%qD{5|uwbWnj&q#uhmczBs& zXh5F7A()Q`Y;+LYBj{0NA4+Dat40kW{M>u+SA!*gM<3<<1BCrM6^3t+D`C}nvRc=L z@47+w#6v(@!N40)uiGe`D`KXj3=-8orG`QjoX;3q`pvNn8H^q2-FHB|30rQTnSeIZ zbtcEUuK@D#Fi1+0Z3Xn~nu_Kk5j5bh3Wq)92!tNS9pVQ^A##PfrYUk_=Q+Inee>(y zp=HmePQkdA*}4vqCqlqe6p@xJZ|#9VxF3svZ0kRandt5B9=iVNOv*n77x4`TJ;@dJ zVujXnPBjb&lGZ-8Tdb{v;XM+W*l&QW>D{|L*%qu+_yTzOd*)0_$>r$XdhGk!8Af7C za00cp9z@M|BlKlPZ62p@%z6?t@{$2x@ypRiKGFL|s? zi(YN>PD)Ze@PNTMgo?wSSj+GhP4%-87SoF&L~8BX=?CP)&5+GR&+qKr7V6pwGvxu= zJr>C%$Hl|ExjY`Ncf?zhXZq*E*0;6WzmYj1`wy=P3zGZ|`d?N=;s}ZBSrOFR#}i>u z^7?mU)4X7lPK>qu^hmswmnheUuDd_M3MOIdau_w=B5v~ZQGzv=3QDX@N{0n?c9eFV zM*v*G|IpqzbdsE4WsTkpS60oS!EvV4q{;-VoL;&uHZB16r5|!MrYmHUQ9~9JnabjIB#W>r z$;u3W8KsO|UNhDscC@Q>)C$J7cS172&=7_>KH|M8^OYum3)@I>olWd!>BMdKr z2e2eNlx97h#$mCfth#@K@ZOU`U}bPSh$j_j2nHkm6M-|S!(c`&309#`ZE`Ukjkj{> ziF7L|cC9MAj((kP6*6QSv>^k3c`~d@I>^7KWmskE7d|xcuO;g7ip4lH4n>$I$>ItxpkYA=rRv9U(iEE#827&7#=Ft10)Srb(|zZ+*wF7yW$wRVar%_3mV4mj5Du-9%;6WWuC zC3#i_wd7e@qYG))AJ9KbFzdZ})d6$rn@JjNo zEZ@pajx>ZNjXcXG4fVDB@6yO@mv(n` zZk#4BPhTwmz{0Cb6{?Yc8W*Yw`2(C)j9)ElkowbGDQCPjxgzqS%`UmDb7No6W>r=e zTY6Zgx~1OnR(^s%uzYEgUMd=Il@6)_FITHn+S3ci>^?Y>ZyG4=*ui=@m`8Af%Mli} zvyC0s1NAl7Xv|H1x3X}=?!SIPWQO%v7%8!$QT^-sBtHfG>*+7!tu(dAz6sWZL4Tm3 zw$=fZ6^olx?G?*vv7pn+@ISjV^{TQ=+OZ#G!uErV(0-V~gHaC-l(}B$W(IXXu12NH zNXcpL4yW-ds1>xEj_L5j1Phu| zMhLG>u!>9kfyTv4OBdKDz<$8U)qvCsnJbO)Thl9+FRMS@`M7O!YyZMk%TZhJWaH9C zwbYIFplH3{Do`B``mG6Rm9i*n7!jR1iz@==Z}(f7QY8F{A0$y?5!?B};II@y55hkS zSc{P>wZfWAv!+`$Nm@Qws$)yhp6ONqGLZpf)$0#1MUh2Et8oyIPPdE&x)AH(Lh!!l z2pAynIouwrgb)>@qiq`lCoG0^Fds4ELbP^&(adfL_KWG=1y(Sbk*PZpzUq~!E>NMm zSN|DS@+#yKmB7eMa@i@z{kF3MIDzeOB0xmHP%m!dUZ@i>7`b?qu8?c-#i_fJTbKt> z6f0CN0&vjW1y(VAu@K};c?pcRovDJw5Qk_ zN7v8AE!tg#`_$Qu-Wp**c?|((D)WI}c-ihq`gXzMk1r$nRF`~F!$P~tPb3t50XpP&KSlNks>=IX>-koKwSW^Q_ z#LyNVWP2}rp7?uk7qa+974_FNEcHuq41H?{pAQ(ea!ge)BfDfgTefZF9Q4gdJr|fG zh2}K}cJpY?4sV}sZSzpW6f4yVNL+oYo?)H4MSjBQ3Q2Z_9bsbBz1YoxFP3wg0G5va zaw&}ryV$oWJ)JuQMwNz04*m%j*0SH6X9 zIPf5h`{!?+>*&VgRO84aAZ-ug2LNOf%+a|62WIp>wf1#l^j%%pAOxo>lS6_Lw1Rrv z?B}Z7^5-81YBUGR0)qRQJjONg@d7JXj|uVYKNa21)|;u;)6dAabf6b%@$-k_96OVsS7y-ag=snZ&8w*~Y< z&`R{_i-iu%$KAabT+V)F1&wT*{4G!#&Umgq)zr&zd=_*`8}>MMra9KdZ6+M))PPV1Lh+4SIP za0p78Y29leRwdFPolg%2tvpJeZcU!GLJ+9BJw*x2@MWW6RT@_qF9u&2{hb@-TBUnt zS(QGJfG}_Bkj)&1EYETwOC>_q7c|&UxrudMzJcsZ5nQQacKJ@Q<5i=JR)}F4$#W=3 z-dZ)QFlKePCsAF1rD8%R)AHHt?WwZ-gG+JY;jY1@pw>ysF&8YNMTFntYa1pzsu^@F zmzc})n=dCQI-vN&je@ht0lY9!h76Dqo!#5IrSHxz#_{?Zf^tTb+y1_I-49M49X{Pk z3-6e1JsXoEnFh8XvKAW=A(MGKBeqidLbdh4=--N|b-s0Y^lyI{{WT(yE=2gCc?+x` zk6xgZ@)ue^9sQeBzGw7rQZJU?uC?wMy?|6sq=yz+o%D23c7Fi2MFRvRs zyvVwD^a906!{Y5z!Knyrclmtlj8qp1268}R@gH7bRgB(HX`)@6qxL(k%+G7x#Ie+P zQ|$tH{OtBJT<7a2i!F#N((VKS_lxgi(ofxP#iuqSW*lS7I-6OVO`a!wY2nBEtUVs8 znq?-_%U_0!pzkbSJRSJJW6{O^)|qs?-#TZE9ls8+)IUJWLskzx7qVuMSrL;>R}Mgu zw12-hBYb?os*j=R2aQ6i-eK(?HiDtUALh{hFTv!_8B~1pvfj;|Z9F8}`y~Vva=xUD z7%ttt!#X}5=~`IX?{8YVyl%znrWOQA>iotq`?8eQ}S>#Wswf6dM@W zNx%MrRhDBnjbTVg(~$0XHrmqvY>oKVTp*d6@ z5!EM0<>!EA=vdh}(~2Q0D=zZ5cb}EB#Cg<`_Rv5(hdZ)t^Jxc-p@;NHnb6{C|{6EfWttJ=~gJ|E0C3V_Zg{F{RED}wSV@d(fWSe zMDs4i&nmtYfG}(}-Scg0iq_b% zUPVAA{OZ?04Hv;>tgWN73u+z)y{f8_r(}x1vTjx}J#ZOT;0qU9wE~E=>~d>d`0b0W zk35<7QiAww+uDRfgnfca({mL@WL^qs({+FLrF#227+K8YrJFI{=5!rZ5>JjbM8W~n zg}^G7{=Mf`Mtxy3{K5Wa@Q@?hKCSms#^6w!-D6#l>tjL3=O5E&6$oOp_f;9(d_4c=5$y@j>TQX^D18srilwBLtu@d29$LCNHG(3dR0OA$n28R8G-LR`L zl!)T~*uSlhb%QGWnNZH!2~%x)pN3~`tRU7AKx%fVGka8s)`T7lp?`{a^1#0$|K*r5 z;qxxHzU`UH%cB=?#JPpC7xg0XN}YXZ@)UuS!8ogWsO`{0bXyx&U6Qj3k)XtH7U917@zFI)aO&J8NmGr}9` zenPCB)7!*i33T4$1W4B7qt}{z850`y1*K8`XrPnT_TOIv*TmIuA`r(A%*3!n7nx6q zms|PSQ0MD;4ufEZ$tmhE!zu(a6`mop2zrtmCbe&ntoG`e_FfHKlk_KFB3vDTx7Pg@ zT9N06tW#-B4$?F{_#2-&IPB_I_ZsZvITU^bMJv=V2m|L{Z0~^zbQ)MLjUj*PeF0Q6 zYJp-zfmU<``U6J;M_#yFqCv(@w!Mt>NP8cI2KDOyfN2b-Fb2srp*w&oIAd^Wgnn^d zKPQ<cj@fv(Zz~TBkD&eU1al7#Yrya(+LJuo(tD zAh@yy|2ZHQH;Qc_HX+Ys)~zm@kc=Xup!oy4#szs~T0pA99_hhu>!YNrAUFHjl`L4D z>Pw{yziA}|o8V!mw#!9I=_aH=n# z-n!CC|EIBVR>MEHI87b>-S)|Iop1-WDa=+)W&6Nz6-@Q{scpZN`hR6uVO&?=MfS2c zt>idd0?gJx?w#WCW#qOFYzqM&VZ*yNRaL=SzG-zMOsYU;(%${Bw(Jb?)H{3Fn}+)K zTP3kfZK2Lwdg9v<*A7ka!-754X< ztE}(Rfvc>{K{uG6p zM;j;6+NzlNaKpE(LmpcEc1#APTn)Xm{919fHJyKf^W%3`4r1j$cQuH?NB)9<=2!2B zVSmXr5FwPZ|LlfatqyY&tO5q;>1(W#p~{PM>BN{=gR-x+%Eq$rK|W#$x;Ikowbmrs z{6h=*(L7MGpsq1_U?*rC5WW#B9Xw&piWVrv2<#P@^MYMY4)>iLgZ-gHkN7fT_%(Jc zp9Zf53vkEdMrHsE5~BrXweOQU5C+J{1J}V=ayR+E3C;e)*8_TXUvEuQ4Lo!`>=o|(7z_zFkTeYWQ1A6tFr!i5+)=1Vowf(A zx8h^%?{xHf=y#l_Pp${3q3LW&%mHI`;EGst&>{7;U3vvamQYxhS!w3hfOvcLUr124GquD7p9LUKk(O)Zt6=O@RGVLl`&z7%~al9cw|k7dq1 z)0dD4wn>nr!~rPyG1TGn4?L*}>Wn@f9>#2Fp}%+lr@mouc)@O~6g@Z@CL7`74lB>Z_ere8p-?vn^@mK4U-!hd2K#xFH_+ ziWPe{Gqh}+SPq}$a(w0A2+)hP1=D>PG9AkW(%8r45eKy@px`9V+b2C{~0%41~I;VPAy(>fMOINPt(S@nAJ1ft!}r zFIv&ExUL?lL6+gQt9mW&1*c`8BE=uFcMC`z<_^&uvzcNo%>M1c5nKXp$7A30=7sON z$tv)Ewwx>}A}13npPv1`brrPKIvuwXRjafzNnb+-pnW%6AJSXo%b*)>vF205(V$yPJv|mX`8HmA^$bLaL-3&zX-8X$HRNn$S zt$Tpe!@!mm9`gUiXVCmdL9hO|Zh#>>tC$`a(|mM%jW0{W1xBz3`ERq%o8t_K$FFdf zuBXFOxMS?uEVEt$I(!@OMfEqaR#)6+6;!E%)zVU5v%Ibm7S#yDUWTAG4N9B8dY}_I z=CPxY+O+m{*h?d+5W-K~X2p1@`$tw>hR%}Vu7xxh$F7Fw+qeNRBO6P71%c=`ikx-j zZ4qgppBzj#Sg1<3+g)O+94`pk5YC{WY{WyzfHqJREH7F+DX^|srR28PW~#pwNtGck zUEBh@iKR^~NPGjl)pWN10{7e6x@Ak3w=Aq_Xj;9hPI>Y`_&ZQMhy%oUHVAuYra0v# zWA@?0;uH+Xl;^|-l1h_4N2T?XoI0bl8-v^qbZCJmtqz0Hl!}cyh7CbZwEm_(g;>9V zg@`QiP|cbwO1{IINw3~+{T+eutprmTX*zr)CQbbecimyl^9aTA=t1ktDNKpO8_w*$#PHI8XVbrXU$#;#019+hLp2`v1I>0`?PF$QMM8j6I5_Wm$~o_g7u6UU$VGASOa{8BpoFl;>v*XroP>@m7cJkeNV zF$cmCz5(Zzma4{R=q_uPPD?U(M|YQ)EocYr_;?HinmpLB@@?8dp1n+ER;*t>v_ zQoIz}6mM>}x7YP-MO9gR-HJu*l5)5wCZ9gM%X-FuxV)YAzJ$BwcXwlN`%^ust!f~A zX3@XRk@3tRlL6^1j-l?A?C9rPpiq_I05<$_BjUTLHFJ1{6v_T-1-AGS{SjN!~wU_TgE!Ihlz2xJ9g^SyD2 zQLpzs0_t^4rf&|-eZtC{$c5RH5az%O%?TDC}UKLRON@>{qRgy5a>s5LGHDQOTHl&6P{5F?mMj~CgO zAGMN9At!0&qoALkddx~OZ~+w2@%yYmw!I`0hA7em%ySnQ8HrAlOh?`HsP#|`SQ;w$ z26*FLk6Bxj86CMQ;h4P&B`&&1PI4O7gIRgPniw{J#XBaP?g zQp@Ysv@;dW?GzRs$te=$xCW^VCo_X}CNh@*`RGz|2Sxx&wJz*#_=m4s{~7atYfOYO z4RH#wIGXTx%L@Pew^n!BDgL$V=Wxu$btRxdimD6dAP!@BHQQybyP$ktad8=5mm$>T zTrM=H*pG;d6FWmY!izqzzGqO&)xNm2)rfc_I#R3|Y=QY^rmtV^%fB4#*g$0g;g`6k4M0+km`|Z*F1)X}`;q%tf#m>p@g7Sc(fa ztUI4-?zAS3i~6>xx8G64>jK$P1spMx`Z_LOzSioj_8tArQO|V6Zy|0u+6Uo6LxS%} zy2BhWz)0Z8{@Jv5rLQEsDACvIp>!qSdVH9Nz z-gFrz9XJjl)P?E3ajCU+3sx`Y^Xs^VOSf^{?dZKu`{#Q_2wM>6POsxL&u%%hZq4AmChulFv0~`Yxqw9%PB(V_q{Bs6gUoQ0tc< z2Y>zNxY{em_zX*sL?@ki%nKiw+q@=yoaws?5n`^-@=fp|%^pIX#?$zleEH$yS-wSa z%jsX!Uo=F&KFi1lMzFF)Z|G)%a!RerU*z-psD60T}qNWvIN97OERUwU4dR#3T zDC_|WmsYiBI;}78A^g;2EYCj*d>x)1Y76)dSgK4oYoc#4RPv$-x8uujsImS5IModX zd>MJI8{xozzC`HmA7F1z#lK1!T2tuzInrQarPV~Lb!N-L=vr_{;wbwgUb-16)dE#M z0cQEYpTX9bZ?odV7ftfTc+zmvB-UU@yR#^i;P+KZj_*-EYQ?s_fD-HjVmX%dvW2j$ zs$hZWr5o7`)-qB-#ZS}VBXMZy*~z~5V79M9a#Q&f_>mSDAz|l0(3h2@l8p0+I2Db{}Ki3bU$)5 zBZ*tyQ983WCOcets_%YJ9Fi9GS2y>R2gC25=3DFa%n7fa+2tI=7 zPG7xUwa#bJ{@MbENG%*4N_p4t)7S;Rv<3fqbM)Ywu(L^6;7f>)^i8kbXq682tuk;4 z#-z?HUtiSRUQ)aRfBBpF=MH&pE|!!`uq@~ti}j2zX8dc$`o=a zf|Ux?CK6Eq5t}R5Ap&Rgt7#k`V>ca7ib*TByOT~-)+dFx(TOH=I9eubgt&`^|6CWW zaFW9mf^fT0oYTRvZdmBcrdN_;GE2&lZ#PsXUC)Nqe%H3{9{W*=w<7gEWrov6&Jd|Z z?HNujpRIZ*aO{jS z+VF3PinaY#_!vF-xM8M8XfD3jRb%v2hOeLuZ5MXH5?GAsAj}v}dE(2kOEqUfq&4uL zn*1x$6@>;w=FNpGmM^cVuLX*g*0EZ4MghKs>1P?fAl*FHmkYt<$lhI~YjppLa6+Sw zFh!+i7U=^c5t}A(h|-gf5J|h2FRdS}X{vJ%alw~ z1*}`Mu&#k~irdY}5=sJTHAdaMc3orybCSLM)YTl^23o+ioO-yU5B6kZRZBT+C2edc z`#WqcWUR1MgKXvMOm8CfJ!B-((E}bMeBVOfLmumb^Uufs>5Fx~;Mpt~RP?j>9&Lhg zL@0!i6`?ABrC9STHdxCfk$pj4c0;hOt997oN(h$x)0G222Ryu4P#Hd2=Ud>Nz+yZ% zd&WY%sqy?)xU+EV65nGU`u6?4l(Erhi{>uz{eRWHdwiW$l{Y>o$vMx{^pd2xH)(s4 zrktd0ZoL65v`w3o7J9!>u$OR>oVJlP2~7&^&~orMzRox^DxD}>MS&_Pg4}$WiH`GX znHh0(RH*Zc^NJs(idQa*7xV?e-*>IG_kQ+s&PiKzX5RPBAMJVeeXqUt+H0@1_FA|5 zhd{vMAe8DS>%6b7CV~X*fbqGKXl>Si^Z67(3}|L zo;Xh9HyOMu>=&KVJZG)xxVdr9@uudLG-Dln%+w>L8;(6}SxUKrdZfxse<{KYE^M%Y z&|?`f<*3+%LqVzW;)CA4=-4klS~-5}A+MyUG1Ep>!T|BzOWp!$TV~a90=5zN9QS5a zk{VSzxzVbsBm%hgssC}@YcDkcjKEugwO}S#y>#50H=A(M@W{v%=$S5XJt;oneIZJP z>Qwv|boK0Cd0pexw|EWt>WQ7;d*A)5{3%sHoCZ>VVv0;>iJ%cijh%U7MlqunOWgq& z3a`B~Kd-W)dH24{_s+k(sj+2#Q-E0N$qujP`u4r>cEDwuSA@`%5gu`*dGX|Y7c688 z0_upa9HcO%MY0E4&bGKBo^jmbl}{tQ7Ckw&p@uei<&!9Q1bL?Ag@R@uP3^ z#$xKrJ7Hl()1E}r#+@m7NJahfBiAc9RiFgzv>V{`x zwd1q?!rK&8AO9C`);Z-{)mEnv)-XT{^v0JUxxDpV-rHlU<_@n&^}W{{RDMIn zo|E1JRrW!zS^e;x-ZapNYd`31nXu*Q*)6M+cf!Kx%m?w$!Y_clw0;<^j_t)95_fnr zV(Rs$Vr5%I$wn*vA9D)|1o1?HMbCz9Lnq1~b&biotEiIi#FmfW`6=%}WW3@rZ$?CY z>GR&y@qxef3gMdN<${W-IM;AF*48hGzY!~|=F=El4K^APGWf#8O5vkXz5BD+{DYtM zVljlyTM<~iTAER@dAId`nGz?Ev;ACMoLnb-;wK(CLp1&#mHSG^4p z^^320rHxh;h6P=;oZJ*b$}iHf^*gTZX89bl7LQMT(o5y3C%+DS`^srAj;=igq3~4o z#w2etX;@>b=%T3icVG8DR?r^oH?Ye5$BzLh77|>~8XQ`0;v*H_Ja%Spb+KS8s~3CT z3ZI6m`vzEnm3$r^uxPfEY)y6Q+a46mpYota`)15jErpP+#V2`-j9%vQX6aSb$6@Ug zB%%Na923~)4y(KWJyt!{bibbDnd-8WFw;!`3?^?Ef7E+%16{dy_4jccP<=4T8~2gj z3Ciofi8ASt1lWw6IUsr3`d7X239l+9T)%3)$D1`1kA@NSqcOn*Jm_jZqHjUCHSYnh zmQH1@sD^0`1loh6`y1=ELU?X-!_Bsqy7wNhanRl=Anr@T3kd2PMk#anR)8UAY%uyD z1Czqo5iD&qc>oVSq;VqR`lo9DOspmYfXCktdo=*CUVY<69J+sYulLJNJ4m2Z<}fZ4 zit6rDUJcA6SlmT=%I%tSG0USfXsw#5aP8D)iuz*Ur}lr`t1Fk0WK>qSecU^wp81$p zbDkAB`>;1-3d|KWa3L^#+sC~1-G%=JP_7K2X(wrXteCe}J^Wm3cC`y-yv?!)SA#0~ zOPKa-_#70#?>*(+da(nk%khl&sK^`{j$kCHnGLDI0~}`igxk#HUx4$2TR!1UpW${D zZa|HG#Ji}_<}X+6?As;rBipS7+FM#suM{rOF;K?3{R%z(n@kb59 zZe;c1`yaPHk6j$_Bb;A3FsG#>vJ6fIs+7wu8aV$fJVHe2rJbw z42w1K8Q7_hNXTM=_rtSn26m-^siR+*r6N^nhG8a64Jf#1679j{X53te5EOEML%jS^=nk|b%Mn zMyypaayzQau@1xw=3c~Q%dXh4d3DEzz>rd&DGv4*EGwSkGp_E?-9V@-=N9*d7BrTLQ*%wW-xWy z_n%6gmXCEjU90AO3a<-3{KbN~jkv#31Xt%847S6DXiOk@Itt()wfR$CMU=_BTK6fh zHnKq7ofoaDJ{~8xYu!6Hb!~^)W>+VELFV7t)ue9woLAG#;x3Y#3tlr_v-AeuR-K*D zhKILHLDY_41n%}zt==`$4UyKH1-zMh^zUQK-V9f|z~xFqSEo0_loCl&@3_xvoB=X2 z1UIr2rgyM!4c-Cvfj1M@xnv2hh+q4JH!Y?U&Md}d=(SIHHBLgQYQNuW4<($q-Kv>3Rzb~GP1-$U#{`-U8 z)Tj)fM;m#_n>7_S{12QI8XFh;+2M!!hs1Lt_wT>WMZrb}F z@)ibDDplWm$Xo7yFL~H&E}$tBw2!OrJq#1X1CMym#H1BUWx%&oGvTV!VlT~ij?hj~ z9gli{J9bx3O#S|Qu~_DcoS$7JRBBOA-sjyBFk%Pg91z6n9O}(r!A7px2qP?gSQy%Q zK6cHy?UQ|@08$lw0(-ynK`3!8^R+j3=lblLuhRP4}Be(bH5(z7Iv&seci^v{;**%GgTLICF;EOSqj?W{npUR(lg$(NP@m0w(n6Y1&2-n33IKLD7+ z%@8*fZqvy$^K@7pB7;9$3fbykoPlrPJHG)NsLL<*W{mw!58|kSDi%BE6d5?ki=%V~ zecRKydE+M1evv>M{1*L&4?vrWlL7h~3cf(O#4ZF26wYg2!7r_-0WpcPpA10V{8h-# zIK6|8n^(YP9^x}=;*0&ny;GA|MLO$cctT5;tN1&;*=b?j8fnO;B5n+HLTg?}6HyvF zEpg_3L1ROvASg(-+BRyHag=@-k*nHsCRS8o870mcN#2+OMmDl`?ahku+FgceBm9h! zAHhz28^L?oHzCS{EOAy^s|pkI=HU{HM=*S@1@suvAD2-U%!^@QT-cJB7bnU_5x@fZ zgHxOy$|f#@FNqW)>`^VLv!1*FN~DNzRkiMZuVhZ*7=lug0%UL~qZTg`RZ)>Kubf1Q z6O=U|04{U!CP)Y`onSOL5=a2JEPfqWB8Qt68)^z{t|oobn^(}1xGpiF9FL%PaR_>X zNe(b3g57m$Yn4}3jovMQjKq$i!~9vC=6c=vxUU7UgT^QccG*yu0Hoq}kbyFAuUEBT zLJ(q+L}eH(m3rGy?z0S|#&FX>s&+Dzum+iM``)k!J^L_12P?5ATd6Lp{Wu7xvfZh$<=}e{)hE8PQ zNu!zy#&6C9h_aK+M0gWxPBcvj5bUWAiNwyxsUfNlN*<8TCg{>mbFMkpEdXTzC~}w1 z+;4`3Ihaa^q;=eh)?TIhME=6aWFZ98TLF6uzb6aCu>sp?DEtL6w^5cskqEJa1qQ)d zI6Y;z&HWm49y2(mdjSGie)Rve94*`}<)Lmo%uDJ_NXA4jkbB02zbvktKQS#^lfgve-PnCz z zfgI^@Ko_cDK$4Nn7Tu{41R2V|NL}@qS2@{%q)Pr6Lc*cX$7*XdiMQR-cGVy1?0#64 zJTe=W6xUpwSJq_9z$G$n4nmZn%N~Y$*Fh&pgUQ#3Z;Gpzd-7*`8s4g;ByUE-jQK*^ zQ7zBHYUAt|V{zNY&u4lIX0f(j^St`fD^zngjbA-l5}D6WsnomYdp{P+DoC`X62eQ$s47DI!XRvUYdfAPS1vfs}P~1 z2Gpv*_bMy!!@SZr&`HHtsyqINS6#o!?9i+dq?={qw7TCw*|)yUn>G~QD46;ztpi5V9Puad^oA9dn~^;r}@IHw24g2OdIw9 z1qX(_l*OE4)gh6LL*lb#hQ4Y15YpU}3KcT4UTjixt%CtaM)SKy$=@q{S#wokpw8L4 z6gh9(s^yEZn)Eqa!o@?&jLzGd3kbdICu&+}bCU-^Mb0^UGkJ&9Ow_JZBx|_0&xfT6 zy|L`rEb9h%!aaYAT|OEM9lb{M0FpfKLDvAMC&>{qm*7fT;Q6(Mk&80KrU#@MaXDG;b~vO(oO5+@;0RQxusb>XkXM>dFo+C$HFE_9=As&|83qD04OLk{==d0n zNJBt4H@=Z-XKFv0OPL#QHar@EW-t~ZdgmF56vB39G3sTE~>zrBXqx%0kWo}Vr=@; zrKF0eP9v9WGjygf!MW~xgt)>_Wf!&gPZSC+Wl~O9fzTP}o*hODLRY=H6M`KIr4Z%@ z0Q?NfSg;}1XWO^v|rwia%fs!HYV zR;Zn8@yi}{`dP0ESVAVe8#=C~kN}-sYddys*a6xKL%>kZ>do6ayS8oU+P)o`?WQ$* zr#y&|HWU-paN>Zs{Mi!pKroyQv*!LA(`4Ta1xg=Ha?QcyVJCV33^n`z6yCj)1FJdg zQbsl&M!{421etKHh+rRCP4>c@)jA&QTDJOLo(}H6l(%i@CY=$a_i*P~$r$qvFm+iI zmDi7KGLkXj(50HZcEa<)%>80Im3ikD)r|P1TlG_K@+>$7+K`0L#8JF|An&T-6;mZU zym(p{fNNqZNGNNr6DG;rC_-%nYr9Ntn1vi`cuTM-WNO?bsq1nqN+aHGAUloV@}pO0 zt%_55jk79la>lAOCd6~q+!5`!%5=WQT_Ths>P41LRIqnQJyjF&(t4bNGO}uoiMb=D z3CxO?t#SK~ZHW5a*}ZN373+31!LSj|0Fu{ZHTg;uf{)%67Y3Yk7Rth}h3PyOaE_bI zFmUGEw=J3(#_ z%fy-CQ$O?yRxm*jb|MZDE`JhAYOXDVVpuw^(^cJ<;q-%CBd$-4Vpm!ZibO}8X0phN zk=1HZLth_Ph7$Uw5squEw)}qu1#5CNxZgb3;j+w*qLL%az;-}ejg-yug9rs=zzB=E z$%V+$M+OxLCWg8mlmYcaQ=l*WUMT(ty`s#<7y; zYsT6(RHx0pmvjtzdkWSIN%&x7%Ujv5Hq%HbH8lVOECsSaE)GSEp_>%al|;>>8=lU? z33aR5KjM|A4`X}g*zC?oP$JYn8j)j#s6fchPWOctLw#p=%{3V!QxF7zo!uUR1u~hP z&P)^BLIWj;lm1nFx_|;88Wq7@x`X6slkgITl_0I1VL63)C&kqfww9MV4u*r_0_B4- zw$PsV9AXI1CMu3z?hO+125G(*QY-)8sO-+U7?PqVX z!z18WK2do4IHc;U-Wm00=?+{(MZ+={_~{`3bQQ92!c+w)HukQK)l1DgsNBs6WU`3K zVRd?g7he?MLqO!wH{qj32E9nyiTwZDLcwd*k9%4i&v zZc_uRpbiiOmK6<;84AHrd0zAYYJ{c){js8x-dN|;)i~-2If+_1XC04R&aDi1@Fy@; z-)u0B-Fm_0;hUSlvaIEWW4CRrNSlR%6{Usmfz@saf z-~@PQ^W8cnrV0 zSpuP5ViY4m#pysQYO{WV) zmD>0Mj3MmKBaya#)UK}kw%1GEU2Dd_`Au&$UxJCkU2^I8u5Ww03dZh!wE~Vgd@52s zc6VD;eezjvb&ElD@=D#`OD3O@?PMNHo;RUwZL^)Bv81oc#vwtXsb4l~wf~ z9O{Q29bnLkEx?&f0LD(fr6Qf8cz+wk*AUVZtU$8r%YN}<4J>Y8-4TQ((5#}I7`hQn zx=Ftfq{Bw;y@!&+jR~Q7s3>XIr;b5#t4AXCMf@~x9=SL|dr}qt)^0eG8lm99u05et zL#RiXh0lQ+t+hTL->v0Fc;L<>IAH`xd*R)d%x=&l{$)j(iq}Re)AlJKHU>=9QzKCE zp=W&>B}cCB80^FB`ymr{{Fo=;9%4a!|B=C74rvU(lKs8crw|~AJg?gsp(huRVQ>+n zP#8~>4;TP1h4)yz9_U+h)B`Ycz>|H9x}HX^d%!5^D&PWMTI%25Sm*jQYTZZPfIFJs z($;)wH+&nxLGXfbO4GcC)>2x;j3KDs&i9wYH<^P2LI1=OvL~F3L7Rt9^$TdQ87)#J zYXg#zhY2$>SQrWqQS4wAgGQa)asx)c+R=a-CEFcomB>5hJV-jcgHL0x)1?B3I z&*l}Xe|Q0#wD7EVak-TQQ9NS^J7G>dno`TodX3XqEx3IYLd!z{p&C5v)m)>IgSN4d zbGMo}eb)?S5`nu$k9FLb>=z&Pa$g1Sg0}X9^TD=sI#BOC>+MMgODN^&jeKE4KWb1! z*P;EiW1FEGz=doSQAz2u!5si{z>{pWnVMJCrFs96OG5I9h^kD|V9A{7C9;9IRXdvx z(CLLulaaj+oTEf@3M6wYEizcDHxdqXW%T64c=WO6;%yc*v)n?dgn<7ZyEY zM6%*0a3j9tfn+}oiFdY48v}KwjZk3?z2rkT7q}J%;7X)Bz^h0Yt!gYk5g9BPs>0RuC?hv35R09`N@W>TxAC4kvpSvGMjfz}@*+ zBIj@IElvOn=!|d|5htn6!wPSU(=DT-HiOsDG#OYJ@@qH$ar&5}^Y;c!v@inTZff=)2II zA*X!MjX!tRL)=CREHxc`&AR?R1lZjM6*&~Za?McJe9xOw1SW2vC)s;FmX?fU)Y*^5 z7QlmU+4RLiw{$f-!# zSi{b=Sq43nXla94TOx$mL`Mz}s(*P7e&KFATQH|~Cx@-47=8y4dzt-=fgfh{NFXdE zJXLP_vsl^Kvu}xwt=Kn7J@|cZ+oqfh3dTt+h`uHv)SsR*ttdP8y0JfX6NeBo95jMR z66dVi{Yd%PU%fnej!O!F$P_Rz7_)qb$f8f1cRYwq5)LuVT^$!tN;Ju&oDtCd?(yj zkJ)6w+`w-!}ItR2w1lK2k<@rj*s|aV9w?`3g$Xd)&E29*DCg@ zyc%`gGv3$6)?O2o=qd^V<8dez*L-I!NoRI+Z{4}RYX|0$h|ficu3lJ1%%#Y1V#uS* zXh9Aa>OFE868+J+t#fftpBvvjw$GZlS!*I}w*fJ^k zl{Fk5G6L#t?6@Xql^V@G?#z99vilXwc&FHQ#EKv#1vXEwW;!#WNQnKDsL^3h|v(xN({o=*DVR39XDYhlBTDr9?Q zYYe25BcobQGqQoj?oK@!;^EDQ`wt-IGUP+_XY0zokVGe0<3v_EFeEE&WHgO<8WjFE zD@zNN5sk_Ii1q?(p)`LP+pmm?jnsyvO1tFGT8jP?}m`cGY*KbFeu2OaqU z1ysjN`8De9{~e3Yqg`=035gb*MEBySBP`YU2qfkl2|oDU=VQ@!^fqy|j13jkU5W>! zObREio3Y=5x{b1ojaq2yj8d?R2FbtF<2zwt^T@wOqD###JdzshMZgu@YHx7rg?6b1 z4HZ$5E2Fjf2$5@ruaokD_UeW|i&ZY8y@Rd@uN*T7y+M#ck`)ozpbEZiu!*B%EgNdo z$=4#~O)_9Q6s@(N;i+-HU(Ml{;65IQ&y**iHJDoF6&=vFY{nAjCq2zxWY06g5N-d1#;98$ zwn3Y)X_|#+N_x7vCnqQ99|0X{!0DKS$Ll`(2h~^p2{DI%_VGwmseO31m+ZTd9%W%C zj;6?l1F>jvBOqkN2QyrXf|KrsE-)B+3CYX#m2qqu<40bho+2G zkU}6ws^h-A3U$?Zq)2W4WW@7@5?-BDrwXH$>X9pauR5E<6J^X{_2r7ljI_I+P+?&? z;F$!gAwqOFml(O-luPnCngY!LS4-Y&QjiY6@VO&=-fm_fS*|AqjVN$*83z&|c-j-x zRM8s<{-7+{nJkotU(H7mkfTdD5(Da>s9poxYStpT?8?}Rr)R6;&ZwUTyAaw&%s61q z0jo*_)eI?m!@)nn{DU%$eM3j^e6%Tk7#4vLHwy?1eP}g%3X@uphHG#FD>oW?(r#83 zYrt$c+#V36H_E2fGDPA-%*1HO6Av$=;{JglSa>$1&8X*@Q2}nuN7As2Zcw&@+3(q- z(2a%XX-||~Et+|xQ+gsANT#etwb0x$iooo~v?7h*I|JDm*UTwP%qXoTx&WXwO-eiV zk;tGjAV)5&P=y!VO(P>e>B@|k*M?dq`MPAe{pxPB%`($4Gd1g{J2#&c!9`@576k~~ z(aHG{#@y00(&$fA7lxKVCxZ+~9)?J0zuVsMXd4ogU~Fel_}~%v3r-Q?LMT!Z9MIcF zY0B(X+c&OBq>VQZIyQ8z-62yVcxqe^ks~HJ_7(lD|0ajL z74CA;sB0j!4#IoD3rvm{jd2Bb;q+26#<(s0$xEUf3chqw z!VoR8K3rZvf%DYXuOUc>jn9G59E&M-dP@^!j&|Chb`rYT$b=+f#~P@xh6Yq|L$ngX zn}b|UA$qs>iQ8v ziIhF-%~h{`*Q@3j+!Kw`m?TbFBg07TNF9dQ2COTO+&Uf9xts{Z;XfQKy*FzS?m8LiBn&4U z7D8jjK)9@blU!$(r)#OVeKfDkH>KE{^?6~kQ_p&BC}KAjbRw`g5Yf^79D%0oJCcaZ zc9ya&zQ@J`r?Kmr0FJFez}t19z%y|LgQfu9^FXP9Mxf3-fs42u00&xLZim*GbCjVK z&^^l%puL|xkvCi2`rcTv`ty(GO?#8Thc_b*rLI=ZiIz_b3Gy@iBW%yBzXNZ+KcD4$Q#$&R!)SF6{jW}+MrdeCE;Gqj3?5nAKiJ2gV|D*H zBb>W>q%S+S*wo!}a5!AA$gZg@srnfp%mIyVN5rG?`+wxU7ExdQu{SB2q*1Bc`lD3^ z2qk||o&K>`;UPlP7W#PqPrR4)!zh>><2i%wu#v2T-2qdeDzYNZDB_NKf+A+3_=!A%$Dh6D>@>rfNoYY%oj2sa%4)+ z87v4BnY}&0nJ*^~PaV?qhy-<>k)+qrcZowEPSiQ8=C zcxOWp5BrQpb^3RBnl}E=)Xguw^6})$-iPz2U=fD~ZcO!c_8%TmPklC8;WsjT zn&Rp^H^r(D^%fUm{3ATSTUs8+APfCltGWhq=ZBu@Kd!eRPG0}IH*eZ-ZRh%J*{#@7 z9!ecPfE$$C10WxzhMLsrseUWj#!gc={@U9a9yFGHmMZFrl-~Y|*QD-y1$4Ij!B|5B zoGlE*;l4NyhgSXA7janUC)uqyejfr35A}_Vr(W@fBdU1;;PJ!Xz%QQ4`vnC4K%!#x z{Vfa>f-x zJn+Q98vB7wwTQbrAUt~^e4^G2?SbgXsYAP@mSO^`t*-*lI)CTASWEaQ$ds7VxbJvt z)1H==`Hii|8=B_e^tv1GPR;BX8}&yp7uiV8u7N}Ek?rOgg;)&1+Q8Xpw4rc9^c;b! z?+&RB|G8Q12>FLHYUit7X#?RIVhB+%9($rWq@CoGC)DZpMdr-hG!zeBap-38ngent zC=NJWifVgDUbM?}t^ao#)0OC8urQY4Vc4LaYlj8~hHl!>KY0C!y8BhHj9%*?xf7nP z4#M_N2u2@&=`)ODr|S#~L#&fAlYt}T_FSrfZJ64o7Q%r@5uYiycD2gW>3Zv=3 zuuPb-2VaZ%bNQ<0Y#h8ELSSn)C+t0B?9k~5l}Zb4ID$K{Hc!u84L-(nKA*f$1ELvB zlS&7Z*2f`D-}sp*oP4);4~aL*uUS>Z2~;b8LAaomS479fW~x=f3p4tta!@R zbx5Jz@Kb{&+8c*b5r!@&6nt#VAiosF3wXr%C)Wt&q}H=W8K%fwt(oBicuibQfC)k4 zNH5jngRi)dcRCM-AHq%xkB`E$*2CQn#u~4OEQxbl67})q0NH9D!|j%$yBeKpCPZbt z8ap^MxL<}l0H`y93T_Hbja^+*^CMM+Dq4n+I+kcCzV-Efgp2Rq`n=T1F4&l0|o*L3pA>4 zjB)CPHKd1<*Qc-#V2(^@3VHaA@_t|wjF^bRR5Dgjolm&+@>5u=&mD^Tm(tp@;h&yA z?LxZk1g3w+?2jai^5g=tcSu{vrnjpLZ%mHxJFfkB4wLGO--IZkP}eeesTes)=khHH zv!dn%6X6S9SE}cK42+PTF&v$59XOJJS~iOgpe}Yh-61ZTH=uK*W0pBQosvUglKu<5aky6(cvj>?N|G72t_pTJA^EsMO_Yj6`CT~9ldn+7Ch zyJBK835p?47722~QYWU2ybD1J?WHpDqD!Paokhwy0WIPng#)r3*KX;e@M~lR2=sOm zJ3F@RTE7X&3q#3kuDwE%7lo3M*137tb`)P6O0QnMy%UK`G7bqm0abOoAmO|7?tD+R zCiA1}_6H(mjr=r}jx?E}ele@R4&XK7$PPRn)qbeeCubB)QfuEDt?b0fjYFT$DA)f@nO)O-qvOgv z7p`j*v`#}{(hX^|rY2fwNJ>>^-Y#D zlec=MtGVzw8rMLl3OPngxTnq}{7chaNAZpl&g1C0Dg6oOu`P8Vb=39ed^I^$wfwC` zpj%=eq+5c^U7I>>YJ*K(y?N&*O!`_XdyAw(RTjT;^Hp8jD6M6l zEy^(p*Kgux_v}G3{aJ1Yd>&n3Gg9?q=ZsV~3s^glJdlwN;*8XVnaN!I%AxC1xR!?c zM0rV8r=~~alg)XCr=TiQW+}(d@g1M4B zu%J`nqKnj6jlZCv2~VTc6E%L_5@*RRgJfPc9opTF_cE}AYK9pl>^ksnj9MS;8+-V7 zK+G%pp)|mTF$-y&E+@$fCv{Y{KM&LREobtl)d|>W|FG*w(rr_W8$600=s$!vH-}Yn z7w#hbe#558J~bi=R3TL%f^zed-2@h=C8+PVh2MIeL_w*yJ`TB&6gYEQz(DY^+z=m( zOl`cz^ANp|$2eBJr8>-bPRhf9`{ClEWcxK(~NI}yn-0#T7%_T{|-ZE_F zg=$RntG9h4G85jS^NK41va)O$9|GIDuIRdEtYk}7E6|iea)Jj1#yXs;o(m9+bBuo* zwe>|0VD^@@MGjFmliwdFdsjuRt@e8*_rjpe(2^N&LhN^{<2ZrAxLG<)FpVM1+1iO$TU*k=YNr!BjHPrto7E-GnNM zc;%k?cziH_=Hf2F1gwVqaGEwq0z=%Mp&l6L6z*uFZFaCwsyXlhtAlj z$Iic<9(cThu4eT3Qa?}P*$AyTHLn{{r+-yY0k23HJXhczmZER|;^Ffzr*({llK^&S z0!Ruv@MZj04sr8vf-N-|9Y6w@#&H{vN$5{}}K z2+(Yy?upltDG;^yIrKe0iDPyM6c4p@AU7-8)vsCg9g%- z<~JtjW#M^yag57!CmQPs(KPwC1U2VmaE=puDrGIJ!!OY?=iQ8l6)1k*LBY5M#q{*! zu)q^~VlX-w+Bs(546Bn19o8cB2T&1nU=|5|m=&4tt{cknxb%>M^^=NYZO>OJx;0%D z92&xV2`_pDX$0tQsY8oov7)&+Lsgd_om)pdhC>iCdpTISb|ey2bdiA#p*c;x zZ${pXxem|HjE81rEWgc+g=$|KtyZh<$(wpvN;4>;d(i01jiNK2@3Li~Hi70w!!Y*v z@2b_YA4H~Amg16~%>;~XfRU5AGCA_nJa0BymODiPhLvYAEzG~MNy~Iy z+{qeyXu~Wk5-6Rj3*a>kZvA7AZm3x>5yz%WhO*@#pu>CK1niow<>m&JF6$o=M51jQ zV(J4fQm#>gnwf*0c`1W9cWptFKnfA6oMLtPGECdHkRiw5>U=lK(Kqy?#Sg~f4Lo`v z0YvLPcvgNmIXFVLcM!DUsrdmczmfJ8Q17bai+Q!FKYYg6+aw`##zSYj^Vz&&bV!=F zdvSKK>tw(5({u6taKIG5nr`{(^y7JTF(H5I(MKaQrn;nXkdr1m=%ec14hEe?N}q1c zpD{Iz4tQTzJtef(#h-`9fav`!tv1UXg-+&i)P!|q^nxu=oYC98LeYznMvUL`;pQO< zF|D-G3Ng+r)9N*yCF(WKnJ(Heoxz$iPOq|xGLxnCWRknasL6CL2odhPOikT z+#C#vJ~^{SZGY7(S9xvuRajxGfmM-q30zm8c7M}b$i_qkv`T8rcVe~tIhh5Vd-&h6 zx>Hr080AmZQV2K6!*hIuz5AtCGPdnj$#sr<*cAb594VBMLxpXAAmbPXAsmT!5OauU zm)43&-EadO&ygoNJg*(;>xOw(p9JhpV=Jys?Zmrm_x9R9*v)`3Y|;ol@n=gNk#uVL zbt6wepY0I8!LXDH>I9MBfdvB3B&sYN2SIPl^AxJ1&&0|*Sy2tMKzM4QRf2LL(35Ey zC-LX3)pTAqasoIk_R_Du^Gk2WvP~TuyYOUBSAwD(O4+Sx*ZU&X|v*>pnBmK-qcy)lF3{J=dD@SvFVDg&h8!Sd1Ow@ zy*bJ!io`N9S&m)Br}P2wez@#~1RuGeSk)w1D zE@ZxfL6NTB~4cxIRSIPMkQ-w9TDkMt6t?pR$htUPFZ;c72{uW(@O5`93@wo?E z=kDLL2RfYqwTa>gU654JkVIK?pG1TqijrvFzmP(la9HF?mfn+St?bf_V}vrX}|`JlJV>#CnU8L3n^ z`~)%8C|$k#ClDCF_GDyoRJc&ejtb*RKRv@~(uc&6rBTCVfmlZ_p&3cC^PV*YmOTOc z_)<+?-Q<9sY*ddHdlhLK23s4`HLH%#x^8%^g%?b+CV>k=#9>d`8Ah2#(AL&2OYWka^)-^)+Tf-v7&b$cNe~$sEZr? z*(&-4it+P8tVj(%kr#an8~W)JUTz{z^Wk;Zed29g&NX+~0B%S<8S}ehatTyhJb#Y5 z*z>EMgp1AZKll90oZoZQ4?VxMN@_Yo(ETeEpA#jvmNg$0ETX3iTJ9?sNaV@id2oEi zhRPi!yD|pBxdnslc1@^}H51(0 zoH1B-3d1nJgOSw9NHW_&&n8JlUP7o-Jp`L*M?o;izWF)*^B$z?vKehfLY;znWHMYnEj(uyQE>aP9y(b;61OsAncEIJ%% zRQNvt2mBO=Zj?+DoLc-%)r45sAzdn9+~QnQH4T9cr=5^Vq6W-w>U*~O_l1#K=KwDH zJPb)P_&?k_OU8EGDvxGz-chFqoUyruj%_|wa{>2P)i&QRRIwMFD=jA#?&-t=;chUT zR8Lj;wN2(ou(wrs1I)b1$_euL7ow#xzKi0z(4ALDj{GN2=LAfj1c=N`G>VLp46w=T zEUqWW#Z6#<04&z_%+eEKRsQb)s?Rt0mD3Cvp@Rkn2$#R%HKsGn4%R~wL{WJWD-T5h@&UkJ9EG0byfsbM&-+ZmCf0W!Ynfc= zwwW;ix|xkOGYckuH0?_VUYPWq; zk5WSDE7l`Ng*4Wi?w%PnG}&ArP9Vco87s2;!V20R2@UC?z1w5%YH31VThDCooc%Jr z%sZohX^vAWS0M;4s3&owV5Z>i>IP<%F)6)CwWvrY z5?&iRphBVuu4|X2SD%*|@SV7p+VO^0oVKf|_lf}uqqT#J1SrNqJy6>(A3lQXj8W%a zA$p~R6@oNe+F50&#qqdg!6yXUzLLybhu<|%n*tcwA`sZ@dmZ#On-Iz|hEXydR9C$2 zjZCStSfrN#&dT8R@@;Q;*Qz^z@9mk&1sEl;`0CZ)d#BWr&%`R7%rdNQ=wZ!_Nw%M5V)9N<>Lk%|oIpumDz7nyZ8K z(4alobdyefqhOJ#W6Z$!Lcp;wPB5@T&`KU|pYP>_&UP?57yFX)npapY3ck~+qs=Rf zDGZwe5uPVnS!C9_dtd9`c`U3TuF_fy>6PGjHJr*u5RKy2mt<+KaZ7?4A$Ze}5h{n| z2Ha&ASp}tGd_Ar)X`P=XHy|>9f*8dbiI%s z4A0VpHd*@0SldL#{BWbe55#t7oO;=tNX|dKm6*z1z*gdNindbvO>8CSnkeLyJXp!j zqVHUY@8Ug|$=q4#{*ga!Yq>qx%^*z%s>|@sa(C6$MqB)cY^$8RiWa0r4d3sVrAY$; zgykJ%u{vvW(B2?Sunt*d8fO0{xCOHZH2z-@_H%4!-WvvMx6$r{8tXp;`)PSK_VdrC zLcBD=<|Q49^nZca4^XJbXTon9y^u_2aBbNeK%>4ea3PfjC$T<5kd(f8neB$lARM6K zmT~}5$v-F}8DQ5_O;+x0#``@9!b%OXvMX`fn>?HB>Qn_{>|CM1-kxf%hD}(=idCQY;;2=O@G@;p{`0#7Pb&(f6Cdqz2qK zN76X2#UZUYplcMbU(GCN5!JEAk0v-^f)8WOoVU?)YHEOb4Ag9z^NKegLLM}igi3|_ zr4pYj*1au_7VW1~qZf?6+$Jv+qCBB%4yJfMTxGnF(Fo#mO~Q4q38!ud7%!{4A_ry$ zHD$ILJ0m9+2IJ&9a8-rV-++XXoB_v8EepE8Opq%#Ze_HIv7bGE&G-WimuMl6L9{*yt9R`G5 zGvk2^lnG=^WGOKY(b2LxFbEG8_l|EQw5_57gpbW&FdfE z_`0_&U-(|hrn6+kX`8S3>fNs)MBf%MFR9ZP7>p-uE_t##pQu4meE^&uD_& zx3J<3h-l0@$hNW+@3j2Is%@d~s}KIjgEII9&r{RB?8W}}C4XmBeQ+Vuzcih7L9}q+({cE4 z@|Y2zfg-uKi9ImM=p^44xl%W1=k}pI*wzxrGHTT8WMWw}vb3+h<*M%8UenbE$hu=k z!m3l~K4M3uS66?_cu)XlHv=g=+cAxx?Sx+w8Bvd~&#$fP?k1lZMsuU_7ew(0+02tn zSADZLZ>Fevj6_5ak_C%=dBd8hRky>T(dRn~W_UWAEOOP%)de%BGNmOrP+@glLw;rJ zAcU!G++)(^^`s^aVrZ89sb#iLHW`BZSd4LVc9(YSRGRmS=fYbDEfq)fc_Ew8W1V>mdi00WK z-fcmipN7iHO39sU^dQ)CaIZ30f)~0R;OITMTg$cRcyN|z#S0{Y4OnBefPbkivw%+X z0sM3#MUTTPVi9aoZQSpOMWdEnmRY8tE1`}ah|@GR1Umz|I7(p+G6X!jA>r3ISn8qc zWPa0#*#h#Bx&BpwHfQ7w2@~z#@t2`fJc!k`9rUA{DmriMjo{3{7AaX+NuRY z0W1yf3HE+UXU>xgBXd<-t^W{H4E5FNe(iY0Oi+;A9gV*>%YQD?JaOY-JvIMt3}Fy0 z_Zm<~Ga*;+@NCpC8^5m3zdxqpMZWeJR5IJYQB_tT5X;@~h&3$S*m33NZSvL>;$LiQ zK@d!Z9$uIfd2>QYfQ#)L?d0617?k!~v;Ak(sxQY1RNI^gTz}X4Q`Hwr{96dL)76Vn z-y5IP=-=t7$<;6@{GYA<>*}hvMyHRTZSxx<>c4+nn++C1bqFUX^!ikN>!Db~*oUqu z9ee8Pf|&S9QC6p;BBu+=7YhS~(wktkNIumNoyxXg>8{qZuf_ZJBZpI}dMGlB^+M_^ z569|Rv19yFiTkvle+hc8;YHEd)KE>e_2F1osY|nydQ{e>#wuOGK8&?qq3C3>?!HN? zwIJlR54Xb``-hHQi%XoV#c^Eet_r*RrV$j8z zByOTD31^@1${?fU_dm8DGHNHl7Eq?5#|}|0r*8d-FIH0%LB~3mo6owZJ(^LGvqoNM zaE4A7s1xi;Ydd5ldLisW@kyv&|F*Yeoi33Je%TC>c%Nh61#rG100h(sx@z}xvDxbD z$D-wH^z?zTJS^*YL4dc6Jy`UL;hym-0H|d{>-NQdR8>6dZ6%C}jf1r~9Z`481~&if zw*^yc;jHw=6atUNIdUw$ht*?YTzIIYyeVf;6;!2fu5X#YvXLI#?cP`4vw!gLD30DedyedFIza#q4F-6y za~Oz>4%QD29|1lP%^~+zp{eE`+ri|Gsd#b_nh|jb(}!poz1NqI0I?3nlcU%rEz%ZV zo|~Z|rZPsgcm8o2Qa$1+pEz^@gSvphz>4(%lm~bp0c|AagZnOCOB{rE6keGH(Po=T5lgyV7R5GTy)_4zxq?JEE93Pzyct)u*Em2CypB!9Pcx7L9?3)N{F<~@ zjwKcdm!uJ!nY66+-hyqf*BXmWuL3oB(BrPhJ%4=|3C%)-MLwLQ=P3Q`nT81!Ij4>v@7*wGdgj(zv1)72Z@euH{)oBtP)LUqk{zf(P26s;Qn@7w)@ z`FYo;)U8+hm+c_3WjI0E>*iTwE(#-?cm!Gk)Pau$xF95_&&><>Hm|su!j+$(DXGiW z`IlxdTDt`wJA|+AJVJh1fGlxTWQ=v1+)M9j)stn>TJ`(&zLy4t4V(<-9&j#_cVPnt zXKco@X6Ohz)dC^y8=}>b91>n#mEx#-7%YkVz!gJ=7p_OqV^0tcgJM`0>KR}Ps-QhO zQ3z}xXQ6a713uZQ=@W#hUy^>ex~Ru* zF1BxWM-VBxd10GMHsn`FcB;SW@vkVM@C2dLN*LivbsSE{4E z{^jcLdi?`xR-eDR0ApXHdiwlZ#?SWo#d-6?lhtG9H3A4Sx|w~z|BPCBjbAfO(->WR zJaW)~cZ8cBKiTiUB{KfgxB8Rvu*p{*^Oue{9r9PU$M^Rx?%97yd$R44wj~SqUvg;* z{<1DzvN+Ydzb)0acyUS{ywh(kF;~HEHel)QZc-n*)8CZ0XO9}Z%dhod$co^(BWm+q zewDwLY<3RoEDAM)FUpy%i_cMibC-Xy`tn`=N>zBbU#Aw_?KkA^VWE2G-Tss)GoNbf zoqpxA^9>g)r$K3YAwrlrFqFgyz(Y;ObI;Ef$0k*5;1s;2z$wTUXhhr+s_2wIvq;}z zB?~0#?#H5ue3nmB^+|slopQ;|%){{`2PdYebVGz%UmvyMs_q^eRlWQ%2&eA)bhL8E z`KE#MHbE8F&WbxG_GW|$%VQ5yHyY|QGN84NXcX;QI&ipS~bN<7*VJ1^~)z43t*5(bv9N| zrmnviu=(x1esQH<9U`3WIMwlc{RKrC!%*pa{c82py?&#*<6eK6nmZDmk&iP?{oo$I z!PtShy<=M8CO{ws8)zhGt(FEcU43CNzj}P`$NVXI>h9?<7Jkotu-$FH4>a#r(_{0- zzkQ$oLY{g$KW~~Uz6XNNEz@JZ`tAch>>~hLl2mT**s`9j+!DP5cw+;ki{mfVdvGY8 z*p)my2wrg+nd;#+0BjIP4h|g|0Oyw?ZV87)U;M^?@CabM)@)cGN2Dn73Ps*;(ewiL zj7NEi7Sj`sFzPVTQb;LXn>S!Kn_LLg*xcEXe{3NjB_m=-mejN*&>d3Ir4p z5d=|2Q837})5VHXL{LNo#EO*vxihm#P~Z1^zyJ3=pEG!&QZMOVv(yj_h zm(wXbE2mvnCut(poBz1y0}p)tfV7utBfdsG@W7muuEhjem-pEy6{ba*`V7kK=Bo9$ zsvUg>Wzyo%eCf0`N;=Nzr0?4sb@R0bq%W8*q8zD~B+wZ5!MD;Nn_99zD~d1E>PG^jA3AYN zS{9{#T*d~9#zgfafk87LNR?JiUQT< z9^*p9mjkuF5WaiDT8K3grO&uvahgv3K;ZDMO9wUe6T=6IL;Tf`1Wwy`W`%T2uQplE zhl%@m^&^4Fi5EvGO+L*Bi>Lh54+Pc~_bQY8g+%ERA0hs!p@zV1N6){DA3C#SnWON#<$MJlyMzdFR+`!#fm_bYiBdpNlyqH)6&Gp4Bd}=mtwT6Gvs9!D76S}w z4FWHf?|en8VU!l@g2YMMxCt!HyOk|%)pZsZ$EY<3T-kYSN2w@_^kJ07=yg(p-YEKM z#6lqR(0ivPF(q2ss5fAN2yul*ZUlPgJH67XV72x(eYjZlfZ8;Hz21Ll363&XowdKA ziyxW(1^jYk^~geH#Ym@f4WjstHcSF*-jp7Y>Jrrs-!g=W?KE5wxZuw#7n&wG+!!SG z*OrvPL2m>dQa-LU#)^L0BnjMU8#q*%WMR0!l;al^m8Rj0K=;~-%l6`2$p5mWDFHFk z48KTmiMD(MMm)TJfV9c4n^>w%oj~sZ`eTJe3PS>=SpNWVX{b7N1%5P7FV%&crF&t9 zm;v@$`vixZY^FMGDg@rx^6d;HiYO^lG)e^lf^^5BIP+-eNYaqBJupDLt4*E2{=sd!N{x}~Ag=~SiN9;~PT-MeYA#ES$>gI&JdNlh z>r3F=<>t?%^+9IoD=}H^kPpfi7i;{1z_RZSA4bA4OVfhF#h0};Bye!Ujts2YN3HgK zP&cu)hBE?r-QW@_Ke)I0bxCknaan}gErB0AAL&3+jgksOL}^Z_K?(@*Q%lB&6f4A+ z82-Qm`G;@zRPatp9Ss$v=Y6I2h4fTP)gTpKyExFIm5Pu$hidKg4;`R3xIVPM`fxS0 zyZX>BtglizhkM|G?uCymQV^qd;U_hQ1u7pKk%yIM2cA(zIZq6Oel|fbo%3VG4>Zyu z@Wi68`XTjn62mm6N#Mepo&thXexS?i9Pfnr3+V`@CfQKFjrI2NAr@t0WkpAX5`yEj zjYZ%$57z&z?1EhpIw?LPTpX?8oxlmZ7Ti|Ic~?ZDGSokCAc=Dyo}~l6(v5S5dncOKN!{- zJ5H3o2oHqgMoKRgN9)vqmqm9G|In~S;Q6#s2c#X*Sz?+d;|R>IZ`h#7xW>2;F+W6| z5rOBnFB+nVG}0>@FN#lU+l;{FRQGBkccons`Uq$3WP zPYr)aSEHk(grpE@f+<2-%T>6RT_ZQ{RaT#T*lP+@tG{m=s6NCb_7F#COGV&@%jFA@ z3wIKiYqEgA*RR@tR=5Fa<6>f%TJ>(?Ao1VY5D0wXlhR#CHEGh4q!6`4L()j~;dat6 z^qp#wMPjiwBLW-0tcu5B6)G56g0%JY$sLQDQ1wuXimFgTe80T=2l7l6ho7W` ztHZgTQXmd&r;dWa{&yH%6GA6hG=$Ev6pF_+S|!lg{ouf+wtlySE4`n`PL{ZTo+@p% zn#E|1+a3U>6Ta&bo;@4VL z0!ziXmr*r~l18TaN#*GQ;zJsXBd{>><8KtQDa&rC{VG}*1z8wO^e2xAIEc-1YOj?-}BQ>^9k^(yl(vA*c;!oN(C$RO? zFSF8%*lb|O$f%jx2Lh)L`}DdG6C#jouZ_=PF&Owuf0-{KRRuA1kTX)u}CY5 z@_nWJ2*!qK+g=Lp6ZlBw z{58r4Y%+RKo!9BAB@ISvVEcZ#0FNz*8?=IVfcpl&I}JR4_(;xLhzv zA(}AcH37>G@nrU^iv-gUuiIg-8C37Bt5*=PPFpDg53W7FMOx4e%SDTqG`b=%BSz{> zmKN(vwM9XqS3?7V*7pjAVx3Z@vujA9`HmPre9>L{t$T)hBwhN)4>)i!u zOphR?)<-CbA0N}nt1J-t@MaHD{6VWpVD^DtOL2<%S}k5-LY*{bO5h{6wk?&m_3k0& zYZ8jUWj9v+A#EJcOU1|c115?SG|^Asw}Xa;Nd3CUNZ0$Cq_cwz($;|?;+I;d1e$s` zke%jGhS&;gd@*+TyGWS68s?}p4MhYFKK^ZukH|+$i9_4PZGCk zLm=?tqPNFM_Q7Rhsn!sIJ6JT?Zxr}?(Mv%k(c(<40D&W_qk6X(hF+Ri5+i141qiHI z<;=aWg&#{aqf8u9Ya}wZ1D2wSAC~%dk5#6hIaF1oQ*cn1Csys2&i7LL&m3x$HV=(d zhksybnfS0ad;$xeC?6?ZAKq77taVP{!jxX`N_|IYmd5H4eZ&V-R01aO_2cK~N>@g7 z7FTH-g20Y_^xdTnBYTN)+KLi*=k?qVrR5{FU2E11~h*OY?37QBTefr&R zrMqQq#n-jL5g3$UJtTE0&lX2(B?-Lp?D@6Q!t!+SwpNnBXa7Cvg7jv2>JW#0NH;OgrI8P*=)CN_BRB@XQvHrM>obV0p|K0@r&8 ztQ0pT3@VQ3_0p+vj|N!Bxm?vYli54b=}9kuqpPSG2+1}^Ld61_hh>?Da9EV93j}>D zgFn>%Y|u+*9mYL%j(iUK95vYX%$-hl=Up z(y`iT^~aC3gZ}YtmbBI7^nD4FTGyp1KlYT?St+<$s}I|AcgihFEpW%v(N{8DD)wfy z{9%xu_V)L^Anhbdt?Re%iJN-LH_F0W`a}?K=~=-y(u(O>aQ(=AFFLerB3?Q+-6{=w zFk{d4y-rp-`%O699mwh-uqVY!Z@eBNJ^Q)|icT`IQt^J{>is+%na-uG-e20%9ESPR zxoBvp67VbSUj1EtM}!=!r)IR*ASTqa#O z+))aA(I{me$&kE9BBX{R6KITb{u=-efh`xgFyj?jf#vccsH-nq@Fo z)ub1A9Nyv<@2y^Uy#p)EF|1x{UNck>_e?q#K*LYVxM0dET{{s0v4w^%Lhv5v2=tt9kI2Bp5Und&_b zlQqR$X|JtvR@uD{PnyYGYp;QVdO9UjX|VZIX}KxisJBJL}r|(vcY7|LT1@e2CbAxTCVyh1q}0#y#5%WYlB3ewr`F@OS64gS z(yO2K)An~ngefnY1tTakGO)qfQ*IHrsAu4KZF~>{Kek#HzefU7>T({_BrG&Zso>pJar37uR()J5yaaRpM!jFNv$EZ<|FjH@ZU5kVPK=ugP zR#L^K;ZQMy3x%S`aGT&8X!w|mg8ikGAZ2`Og`*YfR<6Z6=LhJa`ho~y@n^3O0`?NyP;727$tA0&| z^)zLZ`u;Xrs{1`#`sDW~p>YefDREYXL=&Zr}Mak9td_BJh%r_}7l_vLU< zM}fHb}K$tQo?P0UxBOFgWw1-T-lplpj3o z5&R%fuM=obDcI++v0z}R*tj_cAOEqt-S!z)wGoYlLG4f_-0GKz(3{iwb$VHOoBpNXJuP;h7<)dPN9fa_t1r79t3A!X&eJEjhBRROh-QiLvy z9!zzDO$(_c7!!y4Ktb5b4+VHOj*_M0d-aOGAfvtzNpZz?x5oi@BdDlk6l;sy499`eDVo6l&4d6a#KQ;usn*~hNp*y@ z0aUSMetRzAbW!ts$zbhFw+p1ANKbyd^*J1ZTv9!DRXJP9Q@+<9xW z{KDjtA=J|}9NNN9|FIY$wcYdRIEBNm zokQW4#0DM8&K{sPYqgPv!8Hsi{p?oW0xv$tw}Up@`2Caxik|1Ez?9i+s4Q&fU!`c= zvv0n@=Lu=5Dgd`$;9r9L#e5LdNP+=Q?cl>&Ii}ZDyQ&=4BxJWp_J8TjM#x{T8BRG^ zxL!>KfOGc!i(*BSfNIFFXj{NzNi+DK({XE9O!);S3qF*nTZDYK<-v(w`W$&f88wQM zD&7tQ|1s33up^2}5)`S3!S?Kz+EICsUO}~jtg)0Wh_KFaXwu*+sFnqEDW*c<+$u^> z<;eppC<_H*XEtsEY2d$?s%Ek$stKPbB94gGY7Alw)t+67UjtuiofBq?dXCpwX@aG8 zG-#HC!3Z_+Trm7-r&1wy9F+^d=P>>-eu+K^7=tdD$VUVH7e2$qtNcfy1JbcdD=NFg zm$3UgCQeTr&S0{pn2#O&F|q zP|w1E@st%#_R_b7{o|>@s~ZjSB{u zww7`Q4?dmXFX};a*(!&p((P1yWR`LpoR8)s0`KqN3jULYtZ);`N2do34y$H{DpC`& zE2#*|1|}UF1=pPd4;eq}BQq1to=TUS5LnB?+Ju^vAwnqxRW``p&xM668n!7Btq?5X z(xiFhCSu@QrJ1s@6)bHt5Ee1fIbmWo{_8Cu0Mz0oikh1#kT%hmuzP;sMNn!_7Rq87JO-E&Ga0D^vT`MIYg)W z=LumeR>Xf3+MBRolU(=G!vA?L(t>;83ReD`!IQ$N|8caM@ux0OLB6T24eVA!L}4`| zgiy}^KsQa}#!b_5aS0_PMl@M*>Q3;HEBP1=w^^eOKA1)uSke#NSY-%}ZixtFX1MWu z8VMY1r%Ui7#O|XTIB29=g=wp1AV#W2WLbG&`kju3c0+`a@TOI5Dv+9QY6o$@(=qx< zE@!RPl4ePRnrU>T*;JToQn#KBb-aJ9*p^g`18O=QMI101!L)2TyTzbQ>X2a7XsQcg z@ohkC!z*$ZiZb}iLHFa7Q$Ubfem$T}r zo2<;uq83qBd!^Uqo`Jiv)-lc0Q~iajh-Q^-_zKC!n1q~*ydu9k4CaV@`}0CA(Z=?3Re3!#p{WMMDS>WjhHD!7tDj! zC6rlyyiS_3At}2pDzBE#78`k?s@|m*M-8%EWUs_sq8glI zr`p|`W)7D_*<`tNEj61Bs&UpjYwBy#QOb>TRy(~jq=ScIp>CaE1pZ0Nj&^Un{P~kq zDfRb~(0e^~Uf!~SGBB`f1<%RvY@)U?@;6&hF9qXs)OpGYKRic`mn*kX5~K6=B>(yX zb%kpB3Fmj>Y9&9f7vaSQ$|%40BJ~u*t3(V(w@~5zeLGmu6#f^IUfBo4jH}a}LMYAQ z#(6#ha)t<@zAiS^(6GoSj!hVXEd6}XqVnk-bVj1at@~c!FWdiXNCQL*cNGc`l z)JeQvyHBg9L?&&9HYUlDUA6dI2jILLdAl8mNQYuS*D*0wTFYdHNHbxicsxt+D% z@m5Q+$CQjJ8?2g2he|`vCBgoER7IQ}8`rQ{ZYtGGt@0#OrK`TK8rr>1wWAE+dL3tV z^$qHK7&MGAK>Pv9%92f_U@h%Ftl^kYi*EKf3`E{RWS6>7!=ZdV8=2Q)AEuewU=@Q8 z@rY2ZD5==eQ#V|5nu(*>P=d55@PCU6^=sKVWLE2=Vdhh8lAo_VWt7C(BtLV20y@KB znL8%4Fxg|xoSj+L+E!?ui%Kl|HYbdoQt$FQtf(H=R=M1`&T}p31+^cfUVvxcLT&3x zBid`H-=a>#;!q(4$`j}uaGjzG;Mj3G9*Uo)lOXFCT)D^JMqc%uiw}jl{RDq7zeA1+v0e^2Dj^YJ$&*B6(|S4OQo_@dnovjYD2Y`r+i4YqWm0aDWj9MTeZVp z3qQVxs_B8lRFd4|2xX`L_Av@l-^T^ob&T@Duy2sz8%oek+;<0UgoKM!2E2TLj*4m7 zVt2G{6*f!MAn!!jeE>1}#R+P*y!0gUXei9)lc8}6Z9ylSSuwZBy!_zqaoV5!90A2Q z89lsjV*+96$5bpFx@`;(&x`sxng!j(tOm@`v9c-N1uqX(XX5)ER2+Tf6|BenQ;Nx1nU#-=qrUuWabAlHPof zlMz={dU)z66NIKXEyz3WQlYv;oVbPfA-r}sTBE*SZcJ<@!Vq0b@(G^KVgKiHfaM0r z8$~|{11nKCUVDO1=J1`V4thk> z7Ne)mQAy0^DpcWZODxC!j)8IT4dMEedmbHH}VU6*@Z+z=grJRJs*VorEYT zOs8W*Csez}sbol$zRcF74$Q+Zd5Wv6(uFzPwO*uHLO9L8O0&w5UWkV4^o%9cU~Tcy z2ANJeN+c>v%h!;VK}Q!hx1rd=K5fxpGT_>v=HhbO-7^SnVTUzU?W|rWwqnb0qyNQ< zH2|ZjvGbZ6AuHOl77o9~NXVe?`;3Feo$aurJJYJDePe4JNDo#O4b9Ce$jCr!q?!KV zQ?`OAn__>F$rDr&TzK8i8tXW_N72@-$}S;}!wg(tf)Pl`pfe(gdRtTFM0KILJ7u91 zPsOP7$gJp0`W3QG65&QBjjZVsl?ETTrN?I!=4NJECpf(m>&IoFU6)xpCbK+M`)i%) ztecJ0Z5xM?1?3%lm4%(I$e|-KyaDsCB124LaKALH=WTHO5)~iNq-vW83xM7pj8 z%OIPniM58p&QA0~!bV%eyj;4g5 zt}37-Ifywy2f#Dkkk{Y-j_L+oyVE=1-8$s|*Sgc+Kx`k{02g}DU%-i;bh*B=3gxXS zCB+08y=WKUJ_+_m^qJg_8k97q^r?_IoDP9wHkvaK^FjcSYb*rpt6dea#Y(Sz~KN^`NuhUT-=a;~r~!(#g=R4?O`Mtf52K-b5w*Y#%xel7`d4%823MU@S5J zCbbkA`=aTyw+bUJ;&6e3l_m6e*fxaj4+kBHnHMjhNBVAeRtK|&;B4>Wha?fF$AUr648}OqFfO4DIEr1j-U@hVShFf0*BD8<)=o{ItH?=bVvDmDLsUO`F-h5 zu(F)iK}7;G^B2ldVnx2ii~5PUVKLTZ%7`9h}#A|V#g^C-m%l|7(%4M}xIztHlFkT=`cL@nLJBlZANwTES$t=jHj$Wy*7S5A z7L{@TAKQYlPPz>tlD6=f6T>l=qxnF1eh!}k)%($6Jd;Xi!ruMJjp|E~!v1j502QCn z>mYwJog^QbOn*Qr)DAV+fO$B|f7~T31*Vbc30yG;b)c0Z(pzJL(A5`$moWr?fPr(x zj1_89I328?MR$|;qs2}|8BAo}&2kx~f0t^f-D8C>W-wWaI34mmQPX-R+I>zps~O$Y zc8?8~&0-?->N6q)>e5hlb|1uE;0)i_8AY_ztH+@P_Voop2T`aOJ%>^| ze-RZ*LanfVCY=$B*d@z=909dnuXlzr9PK51J`;D)ZJyOJ=$e4Uhq;gt5`DU_D1JEM zM}_j*JkEz1Vqv9~=jFOtbTLgZ(+V2rAm^wfDq?R;Pk@33WCWvZxDds2k;UFf5keqi z9z7KT=hM-C$Pg9d22FqTh0mvN!`c!i6lN_zX|d=GCpJ%&N`!14wf{ytE4DIYoZpTE zH&@ly)RDL{fxhXJ$R|ef6UQ@Q@Y4c1f>(_SxW1K5zke;G;YAnX#=Vd}4-moCl+~xVv&o_)N}1eY9et67ef4ZB=(T~K4Nsm!Q?=Vh zT;bR1S+l5-hp%`EOqt3iK%a@IQ7MHm<`~IhJ0);%DmF3ZBa8tp+kiop-bSR44S75d zWA;-5EZay2#^7@LoMT8dq*fF9)%g2LV$ke*2g9(Bx-bA!Hw`0Nou8(A!@W_AL4NLO zx-AWpJiNh(ii{&e6*v&|CY>x>o~2_b2xy00ZI5Acp#E7pN4~U`K1@Nst#r10bsPOQ z1sAr{F|6A@Ekk)3h5Q2<4f1^wxG+1L;yE^$8P60*FRe;~=XcPpgHU5pWg;R`VVzRr zk|;THCtah5ZGHGKIC6lV3lRBrI5G_jsnc z+*NDM$Qiyarb8mSV7d|+aix;wg`-sBnN@K8HC)$!ujCUI>7e8-!GKkKCS2WUhzKwv z!ySjp>14u-=Ky8oQvoR`Pv!!H6G?hVqkCVC3-$A`;NBq$nt9^ zTj2fgP*oz-rBgcaOs$1Y6-+uzyG&2uElMGna)OPL6TYYGXv}+&pZJ09Pw%}(XHrmn zoi1X%_6Z(n{E7Y&=H8&Q!z`KPMYE78+I%L? zaF)*w9fztviFTB#--z1{4Hab=nMEiN#nY(!IDV#sl)iPo;-vo$VEu(Y7iG!xz5LH( zxPFu#DIfWju4W+NOZ2bYTgn8(fIsPhV4aFqsj?vpPcouB^EU0Kl@$S85d)a-(N2QM zjy#Hob_3c>iUFW90jz@dl?~>dMcW-!)G(orWH_YP zYsx9Z@H(xG4W@L|hsR-ML<0U*O|1Kx$%CL=J{;0>(Gz#L7djgCXAnQvp=D~RbGV}wVqh+!5 zEFzr@7~1emWGLZkYN6(~;KUEOz@-P!+j8_eK3zRa>*e)4bC*(v1RHcrYdD586tt>P zc!o|CG(H0hzM_XhS3Og#mTo{j9iGrL?f+KAf(rAR^XO6xFfjgv!T~9K?OG%117v^< z5+Im{W88@J6{PjmLgQaG1iAhE9&DmK*U0E8SntO~scWH;&VNgg(_?|*x#FukOu}a zbrg*0hvMV<+ZYzO?LyrtC4^}O$6v>_&B{fiVNM8>2rq`<_CFhhhD6XCsASI!W&BxW z=XtQHj^82Onkd4bcj#DI3S%y?AhyC3l~M1|L2^Ed2ZO31JeEfT z5&SC<$^P#uS37w_3M0_6zl{l^;O0rD5LT9<$bO+UbAchO;&>_(4{>RXA1qI0CW9@F z*#~Sob4JnKcV{r@|8WXIV9aE?z@iK$$@l({@1YNPOHlW&dziM!gR_`S8XjuHbSGK~ z2Manek@C$pjGKalb|~%tm4jXH?tp7on8%PXOW3DOvR+ZBkR$4J6_B@M$4$W1~72zNo*?Y8#)V$l9AobbYd1#!ARz`;&Q}n zkSv(@jey!KX37$mV+@3EWdoqiBDA}SKG`x|7M&B<=5p~+HX4a>nGIJ&A=NiaXs*3vnQRgxEo%u`p%RQlYVoSqjBVg#TY!9-x+21dohiGU3Mef(b^2 z;t=k9gxwX-M;GCw3MN^RWGsm(_#@-ffgo6-ZzeUi6JIN7x*O-bGlX@LDG+`WIDqo zry#=iN|f}+Jv6JB9r`A-176H$EPCXqzUyx6jwd$~=4=@iZ<}snE>Z)MEg3tM8$;9LsqGb}J1T!F%{8&pldMY7d z9%5|X0wyEi{%??al1_lu^BDiY`#At!1-ois%m9*eU(GSS(1V??NwEG%{IFq zcQw1|h!Cw(%jv-JB}^!d_F7hqE&}!}VR{3cV->~62)p(oJMZ^0YCqqtWS%L6#_5>t z1jiX0Jmb_y5VJcO>SkcN{0D!a$6EBD&>MdOIlV3k1r^e9fpl_O3^bm=Rz7~h5Day@ zP)1~;HlD|1|f0!xQ*4|_jkj1Gv`*QAAOn;sjpBx-0#dNz* zFEG31-QO^0Y5471<}`f!9g`*7E;CP1@`)?VW=ekb8uJNF>dVnLnV~$)_>*Z9pb?2l z&6kJse=s@zbJEIc(epR~z3{b3a_?JACFSQUM+{L?DiS{XlNpR*rOvkzz|S1psj%5i zfoV3Xd9S)524Z?L`XFXHs>r_3n(}4;`+Nvi5CyiQ{OBEqV&HL+jUz7R@y-dTI-?gI z_lmluFeeRLABwMLqC#k0bB`Sh$yX55y=iu@e2->-q2b#lOrShkU_V!;3jy7cr%pAn zj&SFMT6CZ*33{G94BI)UA`3IYjbBmqD4>mg@bo&A5uJ=|v|r1jP;O-V#I<~b8mzhH z?>us)W+n{qLxK6h7mP?_Kw^&{I|kmO(4zVFXY^=H_D9M*{|nKw{8=Z& z1h50;>HyY{V$#b~Vfn9!+IIp`Tx|Mcp#fL4khwzm>+~z{1g#$ZO;bb zDUr8=*e>wz5Oxq;HKEi@3uZ;g4#n7wD~|Q!6l)afLfI`XKDP~H!4+0KUR!Z~9 zw4_NQTU4G*(q+neOs{k(d2&`~&3##(DpyuQARg4;sgF_KC^B=X1<^Xx4_Df4VUuBd zXC$bvY-kxITG_o&-WsXznw9k@lMx^?i_L&H)7b)eCkrX^UMkx`?%0}rk|T34LTzjO z6PSs!`7m0mB4d?k3M$gD17ABW=rje6rD6X`U2tz-$!5iD8>URDddq!zD@fS&m?6-! zPyeAkO2!WF(!WplE<<~a#kGU=*>tQyT_=*a3ytrwIdI&H(!Am)oaCD`aGe5AVMfx% zwrma26ghcN4jaV4H|^0&xcnC;P_SUA5Ccsxr30HP|I~qPMM2SvoFJPzu}KsjG3b@c zCLoxc%cdh(pUV!0SARt@a5tA74hQmBfRM?!)oVMm@4=|~7>O92&;ARpB6Uzz>Rw$q7!M;HGwR{Du&eI*?Iunu5lus66$g zv8i1bXg1Z2dF&($1H*sKCyO#H8Tfy*YEw2@$!gV81K5v%H}WYcqavU|!ni)3Tw4k4 zirG2xsbaP*np=~Fs4%CezpHYxql#n@PW3tBptgh^7NRJ7_?D2Zw#;ztAto$DNgXEa zzd7JXK19NW5_YA3v6jK3v zgpCZu;6R6-~w4rU{1%pxn=NQZHlW1XwMg3Z?jOe!D7TIRVOc&XS_N0pXSf^`## zZxGKnki6Ye3ko~CCtXX1A^fk99L)KLFW9#Bp{X!Nc`@qaleaixGCGpmRO_3EHB+mj z6|#`{++T)-=)z_QPBE$vg_~(Sz_^PC7|aHh-{07O)fS?@sWlbh3fK9ENlV2i!=)Wp zP{qQo`uvYwsaR>6JPy01(yw^Z6lCIQ5H=z8A90A2Z~0ygOQb{guV|8th{a>2w=oWZ zs&|v-L6%x+RB=_RnE*|>571>CYjt$9*DCHChr z?aGjtA1T94`bat3iH7YJ>>Yn~xn;`+r6sGq$+;!B@27%-u|p6v<|Q`s3{JEsqoFHL1< z(GYTr%|Z)BfahkgkHEy4>&dDxBMWEuo6JU>zGu_)3*yI#Dv^&mLq$ zD9HW;4}7d#%v#|3WFeLeUoU^Z7|kE~rzPwv5jn?oWFs^gKfyyf?;+wE`Ln}S|^GX zEp9;F&Tx|rYcgnI`6eWWJLj=j*(@#)iZ)|E8#WnvxYEFe$WLr$(Q&%#8Fmq|?Rzv= z8?>4cPOPIPkJ zakhqrPcLJF&i0e&N?-pGi$3CwUG+K`zJ;9(a0(S0{K+NwV(TP>U)7zJ-kR7?Hb(B1 zWB#ye8)JrBV~io4i*W~Pv2qMCH~%0yt=343x1_1&pP)3Dbq5iHdo+?{n`&*rjWNbp zIQBgv>bt|dpZwJ+cDMny3}o|F-$cSqG$Gf|MC{!?#|E;3I@+nQ{1O{0XI*4RP`+0( zzOplrJDnDlp$92R1}!vhixM>hGKvs|HCS7Iv_SD{q``{4s8!c}$ok1C-?QylY2|bl z8oKHQg27Jfxwm0xkW&<*hF(0t4Va7Ea;g}y;4?`GCxGep|VhU!$+j>15 z{sWW0=N~5)!&a7;O+T~4Sg5$gj#tx0SGwsCBbvv^0_~Wj>;dbaxF5P-Lm73Kh#o`Eq@}A4IF(CS&Epgnkz-r)0+#`1 zikmENqqq|k`i*p97<=N@$h}$aFbgmA#N5_OJ(mxsemBHH(K}QS+|hG-nDPngz@rRY z3VdYXydd5|uD0074S_M2F;tNn#Px=SpOJA6*7Hyr#Q7^{m(&|EZuoKClybb1pqB?X zZensJ7V?8j{+Q5kF&h09fuSh+n;Y8@$|XU{RLnFkn#Q$;h75Fo&%I3>yNyVHWJt>u?_@a}Pq$dLaU?g>wVIH62xx^(C0OLJqgd z%ThQy#US?JdWsMV@m8)8u3Nc9I-e&HvJVIm^0PM1L6Hv+$}gpImnn#{;Fx!&b2V~K z1_zYfI-4761lJNiS-QJH=+|^KStZXZOe`e{cLVHoxDOQ5bx0i^Bbn}~>Y|<@R?F4l zog(UCNc;3Y=vu|2P1$hbJ3M5vs59z&B(byFifrDuz5by%uCaP#On$L5XQ1G)j-;3r z;Ap7=bbo%G&m}-b0XG~rcj4mK7NDn%F2tM(@(ackawTy45v0*8`5cB^yW`hmg7FH+z-5x+_<-x+lE=qgSj4XeJ~d)SCnuQQpl|!smhH8wi(<# z*kEILc;1amTt1VV0~2pDadO5iZUW6=76qzwcW2WPtb)~m&L}!wox{z5Hgj?Q*XMF? z!>YwxCY+OyZjUbJ{0yZQXO*JHnW4)9E?M3?j|-(>bqpKED;^~eyt$TZorH>BO4EvH zmRl)ym)=ITv(e_A_Yjw$)1+iM#6HONhrt%J5+z3@$I`vS7t) zxJPSsn84Dwma~Nrz1C;SDfX*6zq%0LOU0R~@>4iFhKVW#}eL8>a}n#PD$CHfWz z#Ok7meG?6R9_IRjE=`vKjSq8Sg0m_$l@yq_h>MZiF5%un*|`#lC;AaAdtwzAuEUcO zxXkG$2rARZ>M)8sfb1i9q$gf>;Wr26dF~@zDd>1(0DQI#-+fifgz9{^pa5mng}~Wm zoIjqp?Xa9%z(LdxMiJhXI6UL{7?Ns37nE0dk8u;!r@9qvl(hXQ2J;@{V#zBW+K%9Z zA@Po z4v+@B9yUy41-a|fTm$F3&ZG?l{f?HExQ(l!j9NO&P)zKF^Zpp@!NVv59L>>11(A2^ z^$l7fQZIzTg@akoa|!VoKF1kR=a8|qXbSJLfZH!{x!@?l&Dd)P_qZ8zx2Ka-{Ul;b zx@xOupxuZ^sW3T$Br>?h=bDo8;I0=fKs*y8Ld9-eor@`0`_wQL8l>kSg=BoXJ82H; z68H$-gS}yYJ9pwz7VqTNf^gCpMnq?z9KD;{5d=TTOtAdfTimTM_~kPc3+FyV7SQT* zt{dil{K198lGEH`sQ3-HGCn2C!@uM*SUCAB7bZV*j%&>-M;qj?zu}^Ea^zJm&md#j zPb1;qcjz#=LFC6Ua3g~63~z?uX{7HX`N5DG%;(D9DEQ3<+i#7vR1{rS=UV^OV9gMWx&qM=1Wd=cM#WsV}sBM0)8AP(Xq;i-X`G$@_*9_&oHoe!;hjM|hG|IQh+=#yF2<*aU37?&9=c&L9~#h2iPd^pdjp^0zjo@X$rUX#4AiDwwwRI%l8U~E!-vY6e zp9ddg;ttzV&L=cgV&P~xA1OB3#>!Sh?u<9)MML0lUI+VrqYaFuW-1(*z((=pvKq|S z+MPm&vxrR-94O~Iz@-zIyn5}b4mI-%{2BZNMr6-b;V7?H@Rx_W6_*C;rd~WRG2ZQ} zLAFs=MwkcYh^c=GW5WXl`KId1@lH1$d%LfW7ccbrOAR@09RCK>`9C!Jx8}5R_cbKP zjpwg3FgzKrT`O&c`qKBcd~a#uHsvg-a`x_A-tvVD*60njQG z6@tTy_^_zd!ZKT~X%1O1!tQH46R=;xYv7B;yoW*03fxue4#IY^Mee?Q5 zJSHb)<-oaz__$DoT@dH2IvOSM6xJ=}O>(b?`G@J?=1Dc$Ci~EyTgSWr&(Pk0N0& z+G31@SC{io!s=CgJZyUuGc^aT=Ch$t!Xj5z@&2-B1^=;n2Oi1r>;MJJ@N$tE%XqVl zW%6lqzuTB~e5^m>*LOVBkOaHd@EL+~Ra2S)GS~98ct~Q?T3q;VALpa-yxWnre3ULj zz04^OwpY>WRedlB_{^@pX#UFK}8ND zU@%WU@+5Dg^Km|^m&ZUZv6wysZNdU#isK{3leF7R93Jo3@klxKZm05{SU<`|CRq*q zJJ5O)pM-PWu!&FhR|e!0JW#$4mtp-TK7~^U8nGFtc6KwL3%_l}knq`kXrMl|lWzL+2dg2d;C`EQ#lA4w-`A2iTC+`Gax*J96I_K z()EU8{7C3~g5Mvc;u+}{?PGEb&I6Grkr_`r$v+Jx=lDnlFB^d==WuxvzTkuLS^~U# zq{&)B_Ke|9E4=s-pOD{Tw~@IjtWvof#Z%&#;Fyj`v$V1%VQ7dnkzq;7#k0d@vuxJV zF>^4Yop+v(;mP5CPYEnPg(xVz%yTg3JRcmQyv6EGhl@~Qr9S4n!PDpXVCyW>q^2Y& z9bwbha<1ZfQ~OtT7xeg;&oL&Ij!7y{O(Gw`einE1flqh?ZN}WCk9j=Nt#k#ezCskm ze}XJ4{R|fAe+E|o%XNf1pP+0gIfGr}&rH2in#>bce8VTksG}!yMOI+W#$hRMGA!n{ zaP}J%U*b7DCDip>oE&$VuP5!IY5qO_{NOUCOaF8k_wuRl(7Nk)nU4|5=9pVypa2Gb z&ks<)xl8CsKMm!Io(tE$=X*o;U0i$gTeN|%ui(mcJ&R9u-=lOH_5^-9tCrt&{bFE7;i^@mkZ-x*ja zPQbK4Y$FNQB?$fWE<8)3UXQArZ1ELgA=ZR$m3)&hVC_DA0_-sflb~y&@D%7n4aT*< zU@rR~iNX*XPg{;l!U_Mnm_{2aNf=~Qj&P~Rn_*M3V1^FyD3&L+5?+M)F7(!)Nfy3` z?Y+>MpB;dIoC^hiSk_S&BAxvq9FDN4K@GJCkHfDPpCr@oJd|(ueo$~;4Rv;0p4_ABdzF@ z+%7y8k%WBu9}D99Bydf_J-DM6MrY>@M^)2y2tB)V_6vTpuD7s+f#Z5EU4FBl@Cgq; zm*Cu2w8xc@N`wk1T8a0tr412&fwMz#8@!Y*WMT@T&=+o{BYM^iMK9N&c1Re@h6yVu zQZEwT9EL8gK_i3!7AjxC#7|c_{^?tWbTp+*m0(w>oU3sNYCKQbmBFw&W<{>5B#&+*h(p1;nwa>t@ zL%Wpp>M<0KR|>~lB%Vbm_s7cUDcVHA!GV~2F=xEcTjHjML-z^7Z1_?zhRQo83P)Lg zHA%6LN(qqZMik%)dO_Y-BUDiLf0ff+!W#@syp1k`=A1T6UDZL{Wc;ICJNRE+9vb+U z%R}MXmrQ#ZNgC6>h^+|oX9>T9wGlIa(H9#6&(9XN=zPY8wDPJ&xt2w>H*P?H+7hHiC4=Mo!$LW1&NrZLyi`~hSZZmytBJVG;Cvn* z6NATul=zUxJENMM7nx>v*J9$8uRxl7da1CMhNqVagLOm6AtE&Hz<;?A5mN6ZF;om3 z)!~f~cqqgM-8VML`NSX%j-2^zwYZRxYn zA)T-f&Ge5S6Kdt!HNp`}ehY+h3SM6)oPx0p!cJm#qaT4x5^pF7f;F3jXt;O*g+cLV zWP~SE(33rTv#=aozv8tcwNDF0!RkdAghLQ5kr+X!rU=P~W|Ic0pAp8gZb$WSIR7;6 z%P+?XLFBf*%4tl#h#y8j>+LtimMd5-!>Q4&(m3&(`K2C^>@Zk?zBOiTvywGWv(5j!3 zeT|;?QZ)~o_%AJHaw$M}Jhg+Wat2-%gpLt{Kyp;Fsd^JKl&HNDyN80^!c?xg0^*2xb`0>u^ z=tR5wim+Tq{7>Yv5IDFOW%(`qBQ2N}zV=mNVH(kWo6~U!W(ZDHBLUS`XKk_S#YUuC zpu&nCxva5xg!ixq&*U8JyoQR70^IuWs3{rvh5l*z4+$TM(mA z%hSQs&8AT*-{8@)T&$oKgd1B?-aTbEyqzIxTkuvpK_BSF`lqP++?#1lilh?!hh`z5!{^D)jSq8CQsZ| z^5?G$@qb-w8CR2M?qA|4XwxW6zW-e;#7@Rrw|o?g)NILvHyVYsbfSmgCdJ$xA0any zu3{u?&7GuT6lxC&@hYcUc2LMoSE!oIAKf=C+HQKrokFIiUD??4pfCmAeoN@ik%sYn zf`JRii)WkrBYa93X(H@z6jJB?ug2!{54YgAzU%*dGsAh}0ak;WL63Eqc!g#ly4KbiUBH7#L&D3j^WQ>!)3=AW z;WXzS!f(@a{1TwxZ4^yi-$8q$;9a4!{OP;GH440M;NCd%zVHp?d?+k{gCG8XwOt8# z6-Bn5dvkNUs`@4*WG5^)3t7k>wh%}lgaCmggd`|P;1WWBNJwG=0>kElE3z5kD6$Uf z_?%G~ecHHz`gGioMF$2&1sB}F1qBrWd4E;+y$PuE#`nGF_er{|tLxU*Rj1DR&wsSB z6uw7WMzMw1rWWmo9uOOnZanZ$EePh=DNc==-Fi43*bZUsi^E!%a=9qVVUxY+BY5#9 z$_3K*0k<0$5g`=tJeVBhyBIp(Mt}sG^|n^%kWVP@9W4&N*exB&Q?M(-mtf;NnmbK) zi3EFj`@OuNYGiTQ@G+jE@l}YgP&}qAS@A~8f&!M9CH0k+ol63+~(jcrXnW+FTVue;eT! zvQ%wJ){lzTdG&zP;D=QP?836lS}FBZdCCe(iz>?t3X5`oSzFq zAXQzndplHBO?~<8nDZdRoUw@#CsF0Oa%6Fp*~!W$-djq$ z%^M(&=YFVt-L~=L1X}SC&Q|Y!q@9xl7>e(~=zd%Su=-NgeMp?(t+lK@a4vfVwiW!N zwGCbzNUOJM;L7?`8)7q__)H6Sz+zDGwN{4h@XN2Ym2DGGB+#*Mw9cBvkVLD-0%Q*S zZb*P4WK0$2%%+RqXr*{*zcjyiPHCHL7PCp@cTkP284HywUJCDm8E&0U>lCj(?iJ>z z4md)%orS6HGUYai<)vUEK;P1!@354~LcCz&7)6Hh8zm`0Own$jF=?l@oLE_E%%wkk z37!@#AWf%n`h>4mI0c;1W>Cu+Z43=Q3z{Q|VODjQDu7OQsGHvX5w($^TyJgYbtnfk zeELd7{_a`r6Q^Q`sGy^d+Isj(2(dUUcm1Rdw6)UNpR^n*Ij22F1rZGD)thh{$UUzm zMq3?%uCclR-lL1b{&+BiLjKYCjpl(nGzO%x}hoY11YLiwm&?Dn&ADcI7}S zKLPzc-gi=NBpXlcXDy3czk-%suLBt{?Pu*KM-vz_=Sxg$wPhUt%J{7kHb1&;_`JiWqAi zOh6s!(&=$Kiw`k1fpSUly+Uh}LD)O_do4gB%+<{^KWc)`2jXb7){hObwih|P^woJS zl6l`H=LM}h-8s-NWS!1}{_Tn!(pXM_!ejdix;|Fxu@q!hEpFMh(qEzu09H4g!+DQPQOuxy*e7|K?dD$kGZSYKN6%$gSW+-7;ij8)uM>hcoe_(2)gVKv{< zRhj2)I>JCrx+5A01q258CwSvPf$iu9n9*^;QilJV-Rx!I$1Vs)S*b?;eVq+DY^;}< zcCR1`hx9!4mvXrk=|HL+x+}j+bLIoetu~Yn+8POrN})Y*fHwzfz<%`+Xuu(f0{c0F z>68;0-xdBkHzWikB>R{==Hjj;?4z}Dzvz*KwAb-cVY??oE#>vJdsk4)L=4fiD zul2Is4l`Az&&ka3|k8FEqy$_3Ka91%2*pukULEYEm%W11h>Z0keJS#QZiKta zQu!m7*HzAq%ig>U!Hyz)`>yP3Sxt)<+9|9C7uBXgm+-&BK@W>Gv#rIedQK*pu7m{yPul~Z-=kcSQ@ zuwq)3$O_fCUmO6J!hj@p*w`G%3heYv5XQ4s&e7;#_PHbnn)Msw{QoF0`>ONlEhZN;-xnB@U4$nvSkN6D3nzySsr2 zIGNI!^%!(nwqMQ`On={ZsyTb7}biugUNPv?_$< zP=(x_-wk15U{515FDEdK0rm`)OdkG?Bm1F32xcJqQ#d|3;x z1RP`AUg%yQAIai`Hy#fTt0#*g80;IxT(sv37O`%iOS1zPvt~4N;~K0B16={Me>97c zPu7*N0d&V`))P0->69?H(YcHTIrLPiN;G}qDY%qD!J6;tY|N@)5w-~V(Yvca3@MLo zD&usZ?5COe;92gV*DF~xy>bPNGT-Eb8|=XH5*P;%_4#UWh+b@Iw37Py>D!)aJ!+gs%Cki-sG}jX}#s| zs3$>_SZ4c^!VXWWCZT87OkyDpmGM4#`+@KFG~O%nmRMO}z9;W9%|^&~a67IbZA`h< zE_ZBE`(<4r0%>Kd=3YCQ#hAZAlUW??n#>ZqnXeD8sc(@zFuVoRALLi_tqx)ymf6qD zGNQdm>oP^YA+J^GzDp#lc3kbjZ~W+iG_b{k;a%aue0#aN|%<^j4zrD;=Gmmu?P z^p^MStSPJjsU>FlSyq`hOksnw+_5t%#}t&KioK_r>F(H(@?pb#qc&zlRi%1K z<}_#MZMEJZd#D!Od7zdhnIm|%mI0?QjU}76tC(^j_kLF`^Dji7|c#8xG!=J6RIFIFA(=rAydc8#Qgi*SCE+(~X~R zX3sc`k`?R`oAJpicF>Q4ufgaZYh#CfSD*>Ji#_kVjiOU)*|WaeF#2pA+vU5BqH7~z z8~0WhZq-v4?Oo5F@ufx4EBCS&&08!6QD(JY z+Qfhxni9_ej(dgOVWa=}9*p(n*Kr3u@g7bn&%Vm8RxCXu(z>szmc`%%+8kS5S~udzHDm*WCjE6P8pT*^;s-IHQ;YhAwk1Y3$sf4m%UVS6%7BYkj4XM!`RQx&?_MJ{G$hhZqR^}IU zc}b-2V~bS->4&bMnyJ{2JvHwD8%-MypvV6BCL0XC9&8DP_W@7S@D|(WPgCB)Np$Sn z>~#y7@Lw0*zM$~^$Rt!KE&J%v;+-FjDaH^?fr;V(}mrDQ$O-0 z3ou50%n&uR(>D-LZ~X-8j;yY+>{FIzqgOsdpWOTrbN@;ak#y>F78!L3t&<#GbF|_c z>D))yKYc$#_?ciTIL??*~5)C~Ob?LV6SO7JD3xVL-X8@V`axlaPq~)2n zPoKuqwPzU!1HPN) z8}MKt?K^>L*PX>mqy3209%;F><0R5<2o*6#=d9y-D26f zh#`FOJlZ+s0wjuVOEB!4D!8_`7EsPNE@GBVUIKg$$d~#@D)}$onkM-0lHeo-7*h+4 zjN&P6o4<{w>-qx1l4s-Tv@Z&<={XgURx9kN{S*8AJK5$@Ojjh#ir^V^-&S1p@&m{w zdT0fv~-S(=U8oIN&qqLUHPz7l~S3cQuU5<7WVr zI64F1ni0}J>!^d5nI(<%=Tqp0`*k<1s?`0Bl{(*Ur+E?;F(HuuOlAF}RZI4Z-rbSM0g~!shvAm~W zYie4ddo0tSuz@)jLPF_%iI+}hoS|z z30g!Hop}n)j!fdCY?2g<5ZoiJ7k|l#a9iU`OQ^A!NBPa4-9RxZyk9sj+>+^RYziei=J$^&pOAc2hT3h;eTo zKjNgl`>|J)-VZ0pRl_h_``iJNsl{cYE5M{Mr(4~G9@T6FxNtHmueyE9H) zXRa2V1HJRe^dzKi^)M^CYlQ!3xZD71P^P$j0UJa}A{oR+`TZ3&)Is{3(>ioPRm}`} zkWEU&Hz_63IjxDw;57hbA}JHLb5$s|R$r-2zpvCLMVs@hWwvA4Yg&gj&6YeL)9?o( z1peT_4n|;n9u#~M!Oj(R&Z~pDP8*8&S8}fZi|=@-fluQ_+pm7M^jb0BOd=B$g_vUr zv}qK-RfYuYf_R|36JC*Dr1`-PEaAcr!ND&5I_dmj2z)ZLW=)~?+*mZm*4V0+6=r}?)N)hXe(U+4MjaZv#9ZWc1Up2;FxOs zH+WETdUCJixcXM(*;)JnM^xfC_{w9hKm<{6DroQ1m0gYyjVRRE(98pwTxSqdE5jJK zfP?zCIRj{UxroX(VMW!b`^{RK_acBcUeEcuI?$}Gz831$@`Z2<-_j!B_bjh^F63{w z^_~|@p2hqh6qJQM!;3KuHd>bO8XGNIgKEM*r~iNv&PA-*5El@F!R zWfbzMgv>31Z>2z_f1&34=(cjhsoBQ4_2iH5~qMA=}7?t`PqXNn3{mNBWf?wGaP7XF~a0sqS^dg(r% zLz{_*^{bf*RpYfLEo8iT12;`86_dk4^vH!*ZlsnblFr}9WBrw#12J!T*BzTRMD@=iTN$;)YQDKr6$$I8} z6^FxtSBhIAqv(&hWNWTM{aM$`{r6{yD(#w@%BHe?MN4NjnZ&kxGX7~!Kla_%*j|{F;(~zhPh5i+D(aw7UrsUt5FdM zNV6tt2x#k-c#?dR2T8LmJ@OqXxRo8V`i2tQUYO$~ErH?~T*AXS#>z~jv`vCkx0y#sMAFFuK=YPQvm@SNC% zV3KW3SD6PQR+M_(x?Nlk^?PoClk`Hez~8-HZ%U4ig80;r(eR9}4r!Utrowdzx>6t4v?$6EbcV0WRN^m8Ymv zH)BPfw+%h({SZUxo|{D={dgCSH^;U?mfCZ(c#AgNAUJ(xz0_zs)fIB|%l`43FA7-QmF;gL9} zcCHr-<-5m+&iI`=m+q{D%;GJX7Ima(gh& z!n-?*1X_z+nt5IqR1ggR&MqOM7u^s74rEz^pOTk?g-MW55N3~+S|8(Rh{$xBGQB+Y z21-LA0MsTx5Wc7p0(?57#C1Vn%u=91{40ac&Zpg@u#tQfDG~xNdk`~PB;+DZO2ZbZ zLOU=~EoHvVzrcF{gr-5l0~Be(+R-9Y7TlBE96MY|p*SAcA#O9qiJC`>=*VU;beo>O zs91_STa7;?2`Zl`y2g8dT^SU8!m8Vh-rh4UexYnbbP;y+$%A>gSM}zll#q}~Ea>&q z>A486@Tw}eyaaaC8<#>b+7p2jdPR-sLMg+=clvB;cY#ZXtlr*{q8MxtnA5)S)!tf1 z|5mNzo0@6tI(rnT!^{J0sp@Xk>uHoJJNvm2!e3K;G;tEWF+#+^drcm%k(2yJirIZS z{vfQj_@t!Y92-SEe*U#|D({ z`i}Byi@aoqv>ws138I(gy;*Y=mZb8@LZe<+iO7!05j6QKk=`+7uwu>WN~f4S>9>rimUjvku>k(qX(BA-Z<4^a8)EU=w|H737|j$s)jCbtna0 zg<-vNvWRw?FHwL8GVB_U$k(jVp~D_gWaW;XBFe1Gxl_at>vGmi@ki=YBl5gu?5T$2 zhzdfZUnBlbTdT#NlO;mI=U@LX#RjF5IJQx9QsxyX=g})rp%3m6xpdon9wQI*Sq4Rm zFq>~xSS%tm@FC;_<&GexBHB3AO~4|3!te_*PWBWvb|+k>k=dkU znw2ENtOR3ul7M>bE<_abDG+eRb{fz164km~vk?(pIx{8HmONDIjT8ZWYCrL)%?Qa6 zqin|d0ixDUwTrkLx&TB_GtcSf@w_ebA-1+z4>BeW5*r<~dY9nq-hqpBul0i2kUfg_ zuh*Q$@h1dgYkh9OcPZ?M*h|Ap;XL@wK@n;!cuHL1p!`al@|Qjcs9@A?(T84$54Z+1>JR6Jnxhw#Gpi3ya$s*>=A-tGf;7BX;T|u7tL>rK!?oIHg;tfy6vO<(3`#Jlu0cBwemXwqVA7jFgXP!)F4 zqntAORZAn;-iP5(*^_SRq;K^L2NqtQ7|n2`2rw`2u~FW!7xtY6{otjX_$NJ{w!5%r zWQ6Kt>8J}8Q&bO#>7bT@#`1lOdY`gfPvh4AbFH z3F}S}?HdNRzN6u~%}xn3c;vdSI4?y;>B%&|2-3xX(RzeYw@{C>`2(y8&_~cxy*s_R zNKbL9`&J5Htk=^|UGz@0!Vj3arCs#>A!YKws}}aejLFsM=_whB#;v9gfd4y!=OFbrL)wv% z-*;5&MU*#5kE8CB^eB1N-%oBJLngrvvJpw%yW}Wq)di}(!w;z)6PaTvW3ui~rzU|f z_MKbw7(e`^;k;GlitOyTws(8WHFm(OioH) z*gJLc;N*$*vs>rSEf_X#Qm=R^2SGU_B@+!!9Vy7N& zqp$v|rDNJlfg1LseF;lMwTO@g&*TeF#`L|4!Dukw~ z;J`|k2I#esUP;EHxBFl?FjrH^tK+CUNj%1yIB6(DWc$X=TlsvO6qK7=1CrC zjD1Icga?C+c+T9GMsLtdf#9bf>-&K<{_tac7@cb7;YQwZz1}a@^vjqS4__egt)=BJ z^!Z>QMaCLgOrFp5SflF+{VhLgISmuX-8b3bBKtBx5NJoZovz4nb)v6N>BWd(tq-+% z=#B41M1*HHqAFnaTgNgF92VuOU~KtT?{5pXQqgB~ilyY8ME28qQh#r?CDSEZY2}*R zjE@8^AZAV(D^B`5%jdQ7dU7nSnQV)s=V;MTGZ~<6w4(^}aPQCT)i`PgG4*ez+Rlbz1k(>C<{D-8oN$1$z%HahwsquTg59ZIGzSn2q5C1_QYWLK_ETAv3d5kxH(1Yyh-f3k8y1k?a(I=Nxh1gH} zlk~z*dJ+ow;wOEj4FwvD&g)m%jDKFxnVsU}T>TyocMZ;>xmm6>%I@b%p$)xVfpk2} z)s;?VyHL|?nBIG2yEIz{C1kt0x2I}2O-`$8$)K6pa9#6e?u2}Q&35(2pO3O#gA=e= zio`+-Pj9lvc>M$66PVH3QY&-N^;s^gkJj&Zp={ zzOGEF=L)8B0PTD=_n zT1RqS879>6se$N(gOZkH{Xj%NK3NK=i2Y4$_$$g-oUwb5>!gidm+<$$iJ!i&0 W#$x_+qZVFk|HB{tFk)!w(EkQB^}VS8 diff --git a/config/application.config.php b/config/application.config.php index b4c14523..bd7c81f4 100644 --- a/config/application.config.php +++ b/config/application.config.php @@ -2,6 +2,9 @@ return array( 'modules' => array( 'Application', + 'DoctrineModule', + 'DoctrineORMModule', + 'DoctrineMongoODMModule', 'Album', //this line is added 'ZhelyanGuglev', diff --git a/config/autoload/doctrineorm.local.php.dist b/config/autoload/doctrineorm.local.php.dist new file mode 100644 index 00000000..f2a49402 --- /dev/null +++ b/config/autoload/doctrineorm.local.php.dist @@ -0,0 +1,22 @@ + array( + 'connection' => array( + // default connection name + 'orm_default' => array( + 'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'params' => array( + 'host' => 'localhost', + 'port' => '3306', // default 3306 find and change in H:\xampp\mysql\bin\my.ini + 'user' => 'root', + 'password' => 'password', + 'dbname' => 'db', + 'charset' => 'utf8', // extra + 'driverOptions' => array( + 1002=>'SET NAMES utf8' + ) + ) + ) + ) + ), +); \ No newline at end of file diff --git a/config/autoload/local.php.appfog.dist b/config/autoload/local.php.appfog.dist new file mode 100644 index 00000000..190d7217 --- /dev/null +++ b/config/autoload/local.php.appfog.dist @@ -0,0 +1,10 @@ + array( + 'dsn' => 'mysql:dbname='.$mysql_config["name"].';host='.$mysql_config["hostname"].';port='.$mysql_config["port"], + 'username' => $mysql_config["username"], + 'password' => $mysql_config["password"], + ) +); \ No newline at end of file diff --git a/data/DoctrineMongoODMModule/Hydrator/.gitignore b/data/DoctrineMongoODMModule/Hydrator/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/data/DoctrineMongoODMModule/Hydrator/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/data/DoctrineMongoODMModule/Proxy/.gitignore b/data/DoctrineMongoODMModule/Proxy/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/data/DoctrineMongoODMModule/Proxy/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/data/DoctrineORMModule/Proxy/.gitignore b/data/DoctrineORMModule/Proxy/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/data/DoctrineORMModule/Proxy/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/fmi.sql b/fmi.sql new file mode 100644 index 00000000..b3bce6cc --- /dev/null +++ b/fmi.sql @@ -0,0 +1,84 @@ +-- MySQL dump 10.13 Distrib 5.5.27, for Win32 (x86) +-- +-- Host: localhost Database: fmi +-- ------------------------------------------------------ +-- Server version 5.5.27 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `album` +-- + +DROP TABLE IF EXISTS `album`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `album` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `artist` varchar(100) NOT NULL, + `title` varchar(100) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `album` +-- + +LOCK TABLES `album` WRITE; +/*!40000 ALTER TABLE `album` DISABLE KEYS */; +INSERT INTO `album` VALUES (1,'The Military Wives','In My Dreams'),(2,'Adele','21'),(3,'Bruce Springsteen','Wrecking Ball (Deluxe)'),(4,'Lana Del Rey','Born To Die'),(5,'Gotye','Making Mirrors'); +/*!40000 ALTER TABLE `album` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `usr_id` int(11) NOT NULL AUTO_INCREMENT, + `usr_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `usr_password` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `usr_email` varchar(60) COLLATE utf8_unicode_ci NOT NULL, + `usrl_id` int(11) DEFAULT NULL, + `lng_id` int(11) DEFAULT NULL, + `usr_active` tinyint(1) NOT NULL, + `usr_question` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `usr_answer` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `usr_picture` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`usr_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'sadfasfsa','fsadfasfas','fsdafs@fsdfs.com',2,0,1,'safasdf','fasdf','fsadfsa'); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2013-05-30 22:47:09 diff --git a/localVirtualHost.conf.distribution b/localVirtualHost.conf.distribution new file mode 100644 index 00000000..4b2d12f8 --- /dev/null +++ b/localVirtualHost.conf.distribution @@ -0,0 +1,13 @@ +# Fmi test in delete me as a professor +Listen 10118 + + DocumentRoot "/PortableApps/PortableGit/projects/deleteme/fmi/public" + + SetEnv ZF2_PATH /xampp/php/pear/ZF2 + SetEnv APPLICATION_ENV development + Order allow,deny + Allow from all + AllowOverride all + Require all granted + + \ No newline at end of file diff --git a/module/Fmi/Module.php b/module/Fmi/Module.php index 8f37d312..e72ba27f 100644 --- a/module/Fmi/Module.php +++ b/module/Fmi/Module.php @@ -1,7 +1,39 @@ -getApplication()->getEventManager(); +// $moduleRouteListener = new ModuleRouteListener(); +// $moduleRouteListener->attach($eventManager); +// } + + public function getConfig() + { + return include __DIR__ . '/config/module.config.php'; + } + + public function getAutoloaderConfig() + { + return array( + 'Zend\Loader\StandardAutoloader' => array( + 'namespaces' => array( + __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, + ), + ), + ); + } +} diff --git a/module/Fmi/config/module.config.php b/module/Fmi/config/module.config.php new file mode 100644 index 00000000..16649714 --- /dev/null +++ b/module/Fmi/config/module.config.php @@ -0,0 +1,79 @@ + array( + 'invokables' => array( + 'Fmi\Controller\Index' => 'Fmi\Controller\IndexController', + ), + ), + // !!! SUPER important use fmi/default grace-drops/in url helper + 'router' => array( + 'routes' => array( + 'fmi' => array( + 'type' => 'Literal', + 'options' => array( + 'route' => '/fmi', + 'defaults' => array( + '__NAMESPACE__' => 'Fmi\Controller', + 'controller' => 'Index', + 'action' => 'index', + ), + ), + 'may_terminate' => true, + 'child_routes' => array( + 'default' => array( + 'type' => 'Segment', + 'options' => array( + 'route' => '/[:controller[/:action[/:id]]]', // !!! SUPER important use grace-drops/default grace-drops/in url helper + 'constraints' => array( + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'id' => '[0-9]*' + ), + 'defaults' => array( + // STOYAn was adding this. You can avoid using it + '__NAMESPACE__' => 'Fmi\Controller', + 'controller' => 'Index', + 'action' => 'index', + ), + ), + ), + ), + ), + ), + ), + 'view_manager' => array( + 'template_map' => array( +// 'layout/rage' => __DIR__ . '/../view/layout/rage.phtml', // layout/layout +// 'layout/waterdrop' => __DIR__ . '/../view/layout/waterdrop.phtml', + ), + 'template_path_stack' => array( + 'grace-drops' => __DIR__ . '/../view' + ), + + 'display_exceptions' => true, + ), + 'doctrine' => array( + 'driver' => array( + // defines an annotation driver with two paths, and names it `my_annotation_driver` + __NAMESPACE__ . '_driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array( + __DIR__ . '/../src/' . __NAMESPACE__ . '/Entity', + ), + ), + + // default metadata driver, aggregates all other drivers into a single one. + // Override `orm_default` only if you know what you're doing + 'orm_default' => array( + 'drivers' => array( + // register `my_annotation_driver` for any entity under namespace `My\Namespace` + __NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver', + ) + ) + ) + ) +); diff --git a/module/Fmi/src/Fmi/Controller/IndexController.php b/module/Fmi/src/Fmi/Controller/IndexController.php new file mode 100644 index 00000000..3320c6dc --- /dev/null +++ b/module/Fmi/src/Fmi/Controller/IndexController.php @@ -0,0 +1,326 @@ +getServiceLocator()->get('doctrine.entitymanager.orm_default'); + // $dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC"; + $dql = "SELECT u FROM Fmi\Entity\User u"; + + $query = $entityManager->createQuery($dql); + $query->setMaxResults(30); + $users = $query->getResult(); + + return new ViewModel(array('users' => $users)); + } + + public function addAction() + { + // 1) Crete the form + // $form = new AlbumForm(); + // $form->get('submit')->setValue('Add'); + // 1.2) with annotations + + $entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + $user = new User; + $builder = new DoctrineAnnotationBuilder($entityManager); + $form = $builder->createForm( $user ); + $form->setHydrator(new DoctrineHydrator($entityManager,'Fmi\Entity\User')); + // it works both ways. With the above line. and the line bellow + //- $form->setHydrator(new DoctrineEntity($entityManager, 'Fmi\Entity\User')); + $send = new Element('send'); + $send->setValue('Add'); // submit + $send->setAttributes(array( + 'type' => 'submit' + )); + $form->add($send); + + // 2) bind the entity + $form->bind($user); + + // do the logic + $request = $this->getRequest(); + if ($request->isPost()) { + // $album = new Album(); + // $form->setInputFilter($album->getInputFilter()); + $form->setData($request->getPost()); + + if ($form->isValid()) { // if it is valid hets populated + // $album->exchangeArray($form->getData()); + // $this->getAlbumTable()->saveAlbum($album); + + // NOW I will need the em + + $entityManager->persist($user); + $entityManager->flush(); + + // Redirect to list of albums + return $this->redirect()->toRoute('fmi'); + } + } + return array('form' => $form); + } + + public function editAction() + { + $id = (int) $this->params()->fromRoute('id', 0); + if (!$id) { + return $this->redirect()->toRoute('fmi', array( + 'action' => 'add' + )); + } + + // Get the Album with the specified id. An exception is thrown + // if it cannot be found, in which case go to the index page. + $entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + try { + // $album = $this->getAlbumTable()->getAlbum($id); + $repository = $entityManager->getRepository('Fmi\Entity\User'); + // $id = (int)$this->params()->fromQuery('id', 1); + $user = $repository->find($id); + } + catch (\Exception $ex) { + return $this->redirect()->toRoute('fmi', array( + 'action' => 'index' + )); + } + + // Create the form + // 2.2) + // $form = new AlbumForm(); + // $form->get('submit')->setAttribute('value', 'Edit'); + // 2.2) + $builder = new DoctrineAnnotationBuilder($entityManager); + $form = $builder->createForm( $user ); + $form->setHydrator(new DoctrineHydrator($entityManager,'Fmi\Entity\User')); + // it works both ways. With the above line. and the line bellow + //- $form->setHydrator(new DoctrineEntity($entityManager, 'Fmi\Entity\User')); + $send = new Element('send'); + $send->setValue('Edit'); // submit + $send->setAttributes(array( + 'type' => 'submit' + )); + $form->add($send); + + // 3) bind + $form->bind($user); + + $request = $this->getRequest(); + if ($request->isPost()) { + // $form->setInputFilter($album->getInputFilter()); + $form->setData($request->getPost()); + + if ($form->isValid()) { + // $this->getAlbumTable()->saveAlbum($form->getData()); + + $entityManager->persist($user); + $entityManager->flush(); + + // Redirect to list of albums + return $this->redirect()->toRoute('fmi'); + } + } + + return array( + 'id' => $id, + 'form' => $form, + ); + } + + public function deleteAction() + { + $id = (int) $this->params()->fromRoute('id', 0); + if (!$id) { + return $this->redirect()->toRoute('fmi'); + } + + $entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + try { + // $album = $this->getAlbumTable()->getAlbum($id); + $repository = $entityManager->getRepository('Fmi\Entity\User'); + // $product = $entityManager->getRepository('Product')->findOneBy(array('name' => $productName)); + // $id = (int)$this->params()->fromQuery('id', 1); + $user = $repository->find($id); + // or $user = $entityManager->find("Fmi\Entity\User", (int)$id); + } + catch (\Exception $ex) { + return $this->redirect()->toRoute('fmi', array( + 'action' => 'index' + )); + } + + $request = $this->getRequest(); + if ($request->isPost()) { + $del = $request->getPost('del', 'No'); + + if ($del == 'Yes') { + $id = (int) $request->getPost('id'); + // $this->getAlbumTable()->deleteAlbum($id); + $user = $repository->find($id); + $entityManager->remove($user); + $entityManager->flush(); + } + + // Redirect to list of albums + return $this->redirect()->toRoute('fmi'); + } + + return array( + 'id' => $id, + 'user' => $user, // $this->getAlbumTable()->getAlbum($id) + ); + } + + public function getManagersAction() + { + // ORM + //read https://github.com/doctrine/DoctrineORMModule + // for example, in a controller: + // Access the Doctrine command line as following + //./vendor/bin/doctrine-module + + // H:\PortableApps\PortableGit\projects\grd>vendor\bin\doctrine-module.bat + // !!! without cli_config bootsyrap etc. + // H:\PortableApps\PortableGit\projects\grd>vendor\bin\doctrine-module.bat orm:schema-tool:create + // ATTENTION: This operation should not be executed in a production environment. + // Creating database schema... + // Database schema created successfully! + // $ vendor\bin\doctrine-module.bat orm:schema-tool:drop --force + // $ vendor\bin\doctrine-module.bat orm:schema-tool:create + // or + // $ vendor\bin\doctrine-module.bat orm:schema-tool:update --force + + + /* + doctrine.connection.orm_default: a Doctrine\DBAL\Connection instance + doctrine.configuration.orm_default: a Doctrine\ORM\Configuration instance + doctrine.driver.orm_default: default mapping driver instance + doctrine.entitymanager.orm_default: the Doctrine\ORM\EntityManager instance + Doctrine\ORM\EntityManager: an alias of doctrine.entitymanager.orm_default + doctrine.eventmanager.orm_default: the Doctrine\Common\EventManager instance + */ + + $em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); + // an alias + // $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager'); + + // ODM + // Read https://github.com/doctrine/DoctrineMongoODMModule + // Usage + // Command Line + // Access the Doctrine command line as following + // ./vendor/bin/doctrine-module + + $dm = $this->getServiceLocator()->get('doctrine.documentmanager.odm_default'); + + return new ViewModel(); + } + + // http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html + // http://framework.zend.com/manual/2.1/en/modules/zend.form.quick-start.html + public function manageUserAction() + { + + $entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); +/* + $user = new User; + $user->setUsrName('Stoyan'); + $user->setUsrPassword('Stoyan'); + $user->setUsrEmail('Stoyan'); + $user->setUsrActive(1); + $entityManager->persist($user); + $entityManager->flush(); + die('I had to create something'); +*/ + // 1) Update +// $repository = $entityManager->getRepository('Fmi\Entity\User'); +// $id = (int)$this->params()->fromQuery('id', 1); +// $user = $repository->find($id); + + // instead I will create a new object + // For new user + $user = new User; + // $entityManager->persist($user); + + // $detachedEntity = unserialize($serializedEntity); +// $detachedEntity = new User; +// $user = $entityManager->merge($detachedEntity); + + // here comes the magic + $builder = new DoctrineAnnotationBuilder($entityManager); + $form = $builder->createForm( $user ); + // var_dump($builder); + // die('After builder'); + $form->setHydrator(new DoctrineHydrator($entityManager,'Fmi\Entity\User')); + // it works both ways. With the above line. and the line bellow + //- $form->setHydrator(new DoctrineEntity($entityManager, 'Fmi\Entity\User')); + $form->bind($user); + + $send = new Element('send'); + $send->setValue('Submit'); + $send->setAttributes(array( + 'type' => 'submit' + )); + $form->add($send); + + $viewModel = new ViewModel(); + $viewModel->setVariable('form',$form); + return $viewModel; + } + + public function changeRageAction() + { + $viewModel = new ViewModel(); + $this->layout('layout/rage'); // change the layout. DOn't forget to add it in module.config.php + return $viewModel; + } + + public function changeWaterdropAction() + { + $viewModel = new ViewModel(); + $this->layout('layout/waterdrop'); // change the layout. DOn't forget to add it in module.config.php + return $viewModel; + } +} + +/* + public function studentAction() { + $viewModel = new ViewModel(); + // $viewModel->setTemplate('layout/custom'); + $this->layout('layout/student'); // change the layout. DOn't forget to add it in modeule.config.php + return $viewModel; + } + + public function changeAction() { + $viewModel = new ViewModel(); + $this->layout('layout/FmiStudent'); // change the layout. DOn't forget to add it in module.config.php + return $viewModel; + } +*/ diff --git a/module/Fmi/src/Fmi/Entity/Album.php b/module/Fmi/src/Fmi/Entity/Album.php new file mode 100644 index 00000000..812ac105 --- /dev/null +++ b/module/Fmi/src/Fmi/Entity/Album.php @@ -0,0 +1,100 @@ +artist = $artist; + + return $this; + } + + /** + * Get artist + * + * @return string + */ + public function getArtist() + { + return $this->artist; + } + + /** + * Set title + * + * @param string $title + * @return Album + */ + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + /** + * Get title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } +} diff --git a/module/Fmi/src/Fmi/Entity/Repository/UserRepository.php b/module/Fmi/src/Fmi/Entity/Repository/UserRepository.php new file mode 100644 index 00000000..f03c55a1 --- /dev/null +++ b/module/Fmi/src/Fmi/Entity/Repository/UserRepository.php @@ -0,0 +1,77 @@ +getEntityManager()->getRepository('Bug') we have to adjust the metadata slightly. +// http://stackoverflow.com/questions/10481916/the-method-name-must-start-with-either-findby-or-findoneby-uncaught-exception + +class UserRepository extends EntityRepository +{ + public function getRolesArray($number = 30) + { +// $dql = "SELECT b, e, r, p FROM \GraceDrops\Entity\User b JOIN b.engineer e ". +// "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC"; +// $query = $this->getEntityManager()->createQuery($dql); +// $query->setMaxResults($number); +// return $query->getArrayResult(); + return array(); + } + // There are already findBy or findOneBy! +/* + public function getRecentBugs($number = 30) + { + $dql = "SELECT b, e, r FROM \Application\Entity\Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC"; + + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getResult(); + } + + // findBy or findOneBy! + public function findByRecentBugs($number = 30) + { + $dql = "SELECT b, e, r FROM \Application\Entity\Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC"; + + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getResult(); + } + + public function getRecentBugsArray($number = 30) + { + $dql = "SELECT b, e, r, p FROM \Application\Entity\Bug b JOIN b.engineer e ". + "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC"; + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getArrayResult(); + } + + public function getUsersBugs($userId, $number = 15) + { + $dql = "SELECT b, e, r FROM \Application\Entity\Bug b JOIN b.engineer e JOIN b.reporter r ". + "WHERE b.status = 'OPEN' AND e.id = ?1 OR r.id = ?1 ORDER BY b.created DESC"; + + return $this->getEntityManager()->createQuery($dql) + ->setParameter(1, $userId) + ->setMaxResults($number) + ->getResult(); + } + + public function getOpenBugsByProduct() + { + $dql = "SELECT p.id, p.name, count(b.id) AS openBugs FROM \Application\Entity\Bug b ". + "JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id"; + return $this->getEntityManager()->createQuery($dql)->getScalarResult(); + } +*/ +} + +/* +// Fatal error: Uncaught exception 'BadMethodCallException' with message 'Undefined + method 'getRecentBugs'. The method name must start with either findBy or findOn +eBy!' in H:\xampp\php\pear\Doctrine\ORM\EntityRepository.php on line 196 +*/ \ No newline at end of file diff --git a/module/Fmi/src/Fmi/Entity/User.php b/module/Fmi/src/Fmi/Entity/User.php new file mode 100644 index 00000000..bfe10ffc --- /dev/null +++ b/module/Fmi/src/Fmi/Entity/User.php @@ -0,0 +1,392 @@ +usrName = $usrName; + + return $this; + } + + /** + * Get usrName + * + * @return string + */ + public function getUsrName() + { + return $this->usrName; + } + + /** + * Set usrPassword + * + * @param string $usrPassword + * @return Users + */ + public function setUsrPassword($usrPassword) + { + $this->usrPassword = $usrPassword; + + return $this; + } + + /** + * Get usrPassword + * + * @return string + */ + public function getUsrPassword() + { + return $this->usrPassword; + } + + /** + * Set usrEmail + * + * @param string $usrEmail + * @return Users + */ + public function setUsrEmail($usrEmail) + { + $this->usrEmail = $usrEmail; + + return $this; + } + + /** + * Get usrEmail + * + * @return string + */ + public function getUsrEmail() + { + return $this->usrEmail; + } + + /** + * Set usrlId + * + * @param integer $usrlId + * @return Users + */ + public function setUsrlId($usrlId) + { + $this->usrlId = $usrlId; + + return $this; + } + + /** + * Get usrlId + * + * @return integer + */ + public function getUsrlId() + { + return $this->usrlId; + } + + /** + * Set lngId + * + * @param integer $lngId + * @return Users + */ + public function setLngId($lngId) + { + $this->lngId = $lngId; + + return $this; + } + + /** + * Get lngId + * + * @return integer + */ + public function getLngId() + { + return $this->lngId; + } + + /** + * Set usrActive + * + * @param boolean $usrActive + * @return Users + */ + public function setUsrActive($usrActive) + { + $this->usrActive = $usrActive; + + return $this; + } + + /** + * Get usrActive + * + * @return boolean + */ + public function getUsrActive() + { + return $this->usrActive; + } + + /** + * Set usrQuestion + * + * @param string $usrQuestion + * @return Users + */ + public function setUsrQuestion($usrQuestion) + { + $this->usrQuestion = $usrQuestion; + + return $this; + } + + /** + * Get usrQuestion + * + * @return string + */ + public function getUsrQuestion() + { + return $this->usrQuestion; + } + + /** + * Set usrAnswer + * + * @param string $usrAnswer + * @return Users + */ + public function setUsrAnswer($usrAnswer) + { + $this->usrAnswer = $usrAnswer; + + return $this; + } + + /** + * Get usrAnswer + * + * @return string + */ + public function getUsrAnswer() + { + return $this->usrAnswer; + } + + /** + * Set usrPicture + * + * @param string $usrPicture + * @return Users + */ + public function setUsrPicture($usrPicture) + { + $this->usrPicture = $usrPicture; + + return $this; + } + + /** + * Get usrPicture + * + * @return string + */ + public function getUsrPicture() + { + return $this->usrPicture; + } + + /** + * Get usrId + * + * @return integer + */ + public function getUsrId() + { + return $this->usrId; + } +} + +/* +Using Annotations¶ +Creating a complete forms solution can often be tedious: you’ll create some domain model object, an input filter for validating it, a form object for providing a representation for it, and potentially a hydrator for mapping the form elements and fieldsets to the domain model. Wouldn’t it be nice to have a central place to define all of these? + +Annotations allow us to solve this problem. You can define the following behaviors with the shipped annotations in Zend\Form: + +AllowEmpty: mark an input as allowing an empty value. This annotation does not require a value. +Attributes: specify the form, fieldset, or element attributes. This annotation requires an associative array of values, in a JSON object format: @Attributes({"class":"zend_form","type":"text"}). +ComposedObject: specify another object with annotations to parse. Typically, this is used if a property references another object, which will then be added to your form as an additional fieldset. Expects a string value indicating the class for the object being composed. +ErrorMessage: specify the error message to return for an element in the case of a failed validation. Expects a string value. +Exclude: mark a property to exclude from the form or fieldset. This annotation does not require a value. +Filter: provide a specification for a filter to use on a given element. Expects an associative array of values, with a “name” key pointing to a string filter name, and an “options” key pointing to an associative array of filter options for the constructor: @Filter({"name": "Boolean", "options": {"casting":true}}). This annotation may be specified multiple times. +Flags: flags to pass to the fieldset or form composing an element or fieldset; these are usually used to specify the name or priority. The annotation expects an associative array: @Flags({"priority": 100}). +Hydrator: specify the hydrator class to use for this given form or fieldset. A string value is expected. +InputFilter: specify the input filter class to use for this given form or fieldset. A string value is expected. +Input: specify the input class to use for this given element. A string value is expected. +Name: specify the name of the current element, fieldset, or form. A string value is expected. +Options: options to pass to the fieldset or form that are used to inform behavior – things that are not attributes; e.g. labels, CAPTCHA adapters, etc. The annotation expects an associative array: @Options({"label": "Username:"}). +Required: indicate whether an element is required. A boolean value is expected. By default, all elements are required, so this annotation is mainly present to allow disabling a requirement. +Type: indicate the class to use for the current element, fieldset, or form. A string value is expected. +Validator: provide a specification for a validator to use on a given element. Expects an associative array of values, with a “name” key pointing to a string validator name, and an “options” key pointing to an associative array of validator options for the constructor: @Validator({"name": "StringLength", "options": {"min":3, "max": 25}}). This annotation may be specified multiple times. +To use annotations, you simply include them in your class and/or property docblocks. Annotation names will be resolved according to the import statements in your class; as such, you can make them as long or as short as you want depending on what you import. + +Note + +Form annotations require Doctrine\Common, which contains an annotation parsering engine. The simplest way to install Doctrine\Common is if you are using Composer; simply update your composer.json and add the following line to the require section: + +"doctrine/common": ">=2.1", +Then run php composer.phar update to install the dependency. + +If you’re not using Composer, visit the Doctrine project website for more details on installation. + +// * @ORM\Table(name="users") +// * @ORM\Entity +// * @Annotation\Name("users") +// * @Annotation\Hydrator("Zend\Stdlib\Hydrator\ClassMethods") + +// * @ORM\Table(name="users") +// * @ORM\Entity(repositoryClass="Fmi\Entity\Repository\UserRepository") +// * @Annotation\Name("user") +// * @Annotation\Hydrator("DoctrineModule\Stdlib\Hydrator\DoctrineObject") + +*/ diff --git a/module/Fmi/view/fmi/index/add.phtml b/module/Fmi/view/fmi/index/add.phtml new file mode 100644 index 00000000..84c26872 --- /dev/null +++ b/module/Fmi/view/fmi/index/add.phtml @@ -0,0 +1,12 @@ +I am in add.phtml GraceDrops Controller'; +$form = $this->form; +$form->prepare(); +// Assuming the "contact/process" route exists... +$form->setAttribute('action', $this->url('fmi/default', array('controller' => 'Index', 'action' => 'add'))); //'contact/process')); +// Set the method attribute for the form +$form->setAttribute('method', 'post'); + +echo $this->form()->openTag($form); +echo $this->formCollection($form); +echo $this->form()->closeTag(); \ No newline at end of file diff --git a/module/Fmi/view/fmi/index/change-rage.phtml b/module/Fmi/view/fmi/index/change-rage.phtml new file mode 100644 index 00000000..e69de29b diff --git a/module/Fmi/view/fmi/index/change-waterdrop.phtml b/module/Fmi/view/fmi/index/change-waterdrop.phtml new file mode 100644 index 00000000..e69de29b diff --git a/module/Fmi/view/fmi/index/delete.phtml b/module/Fmi/view/fmi/index/delete.phtml new file mode 100644 index 00000000..f8de3c0c --- /dev/null +++ b/module/Fmi/view/fmi/index/delete.phtml @@ -0,0 +1,27 @@ +I am in delete.phtml GraceDrops Controller'; + +// module/Album/view/album/album/delete.phtml: + +$title = 'Delete user'; +$this->headTitle($title); +?> +

escapeHtml($title); ?>

+ +

Are you sure that you want to delete + 'escapeHtml($user->getUsrName()); ?>' by + 'escapeHtml($user->getUsrPassword()); ?>'? +

+url('fmi/default', array( + 'action' => 'delete', + 'id' => $this->id, +)); +?> +
+
+ + + +
+
\ No newline at end of file diff --git a/module/Fmi/view/fmi/index/edit.phtml b/module/Fmi/view/fmi/index/edit.phtml new file mode 100644 index 00000000..99035e2b --- /dev/null +++ b/module/Fmi/view/fmi/index/edit.phtml @@ -0,0 +1,13 @@ +I am in edit.phtml GraceDrops Controller'; + +$form = $this->form; +$form->prepare(); +// Assuming the "contact/process" route exists... +$form->setAttribute('action', $this->url('fmi/default', array('controller' => 'Index', 'action' => 'edit', 'id' => $this->id))); //'contact/process')); +// Set the method attribute for the form +$form->setAttribute('method', 'post'); + +echo $this->form()->openTag($form); +echo $this->formCollection($form); +echo $this->form()->closeTag(); \ No newline at end of file diff --git a/module/Fmi/view/fmi/index/get-managers.phtml b/module/Fmi/view/fmi/index/get-managers.phtml new file mode 100644 index 00000000..60cc52aa --- /dev/null +++ b/module/Fmi/view/fmi/index/get-managers.phtml @@ -0,0 +1,2 @@ +I am in get-managers.phtml GraceDrops Controller'; \ No newline at end of file diff --git a/module/Fmi/view/fmi/index/index.phtml b/module/Fmi/view/fmi/index/index.phtml new file mode 100644 index 00000000..79106f5f --- /dev/null +++ b/module/Fmi/view/fmi/index/index.phtml @@ -0,0 +1,40 @@ +I am in index.phtml GraceDrops Controller'; +/* +foreach($users AS $user) { + echo $user->getDescription()." - ".$user->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$user->getReporter()->name."\n"; + echo " Assigned to: ".$user->getEngineer()->name."\n"; + foreach($user->getProducts() AS $product) { + echo " Platform: ".$product->name."\n"; + } + echo "\n"; +} +*/ +$title = 'My users'; +$this->headTitle($title); +?> +

escapeHtml($title); ?>

+
+ + + + + + + + + + + + + + +
NamePassword 
escapeHtml($user->getUsrName());?>escapeHtml($user->getUsrPassword());?> + Edit + Delete +
\ No newline at end of file diff --git a/module/Fmi/view/fmi/index/manage-user.phtml b/module/Fmi/view/fmi/index/manage-user.phtml new file mode 100644 index 00000000..824fc114 --- /dev/null +++ b/module/Fmi/view/fmi/index/manage-user.phtml @@ -0,0 +1,15 @@ +I am in manage-user.phtml Index Controller'; + +$form = $this->form; +$form->prepare(); +// Assuming the "contact/process" route exists... +$form->setAttribute('action', $this->url('grace-drops/default', array('controller' => 'Index', 'action' => 'manage-user'))); //'contact/process')); +// Set the method attribute for the form +$form->setAttribute('method', 'post'); + +echo $this->form()->openTag($form); +echo $this->formCollection($form); +echo $this->form()->closeTag(); + +?> \ No newline at end of file diff --git a/public/htaccess.appfog b/public/htaccess.appfog new file mode 100644 index 00000000..967bd3e9 --- /dev/null +++ b/public/htaccess.appfog @@ -0,0 +1,9 @@ +SetEnv APPLICATION_ENV production + +Options +FollowSymLinks +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} -s [OR] +RewriteCond %{REQUEST_FILENAME} -l [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^.*$ - [NC,L] +RewriteRule ^.*$ index.php [NC,L] \ No newline at end of file diff --git a/public/htaccess.localdev b/public/htaccess.localdev new file mode 100644 index 00000000..221c82c9 --- /dev/null +++ b/public/htaccess.localdev @@ -0,0 +1,6 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} -s [OR] +RewriteCond %{REQUEST_FILENAME} -l [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^.*$ - [NC,L] +RewriteRule ^.*$ index.php [NC,L] \ No newline at end of file diff --git a/public/htaccess.zend.phpcloud b/public/htaccess.zend.phpcloud new file mode 100644 index 00000000..baca7fac --- /dev/null +++ b/public/htaccess.zend.phpcloud @@ -0,0 +1,7 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} -s [OR] +RewriteCond %{REQUEST_FILENAME} -l [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteBase /fmi +RewriteRule ^.*$ - [NC,L] +RewriteRule ^.*$ index.php [NC,L] \ No newline at end of file diff --git a/vendor/bin/classmap_generator.php b/vendor/bin/classmap_generator.php new file mode 100644 index 00000000..f5a0df25 --- /dev/null +++ b/vendor/bin/classmap_generator.php @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +SRC_DIR="`pwd`" +cd "`dirname "$0"`" +cd "../zendframework/zendframework/bin" +BIN_TARGET="`pwd`/classmap_generator.php" +cd "$SRC_DIR" +"$BIN_TARGET" "$@" diff --git a/vendor/bin/classmap_generator.php.bat b/vendor/bin/classmap_generator.php.bat new file mode 100644 index 00000000..d3202326 --- /dev/null +++ b/vendor/bin/classmap_generator.php.bat @@ -0,0 +1,3 @@ +@ECHO OFF +SET BIN_TARGET=%~dp0\"../zendframework/zendframework/bin"\classmap_generator.php +php "%BIN_TARGET%" %* diff --git a/vendor/bin/doctrine b/vendor/bin/doctrine new file mode 100644 index 00000000..0eb0b66d --- /dev/null +++ b/vendor/bin/doctrine @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +SRC_DIR="`pwd`" +cd "`dirname "$0"`" +cd "../doctrine/orm/bin" +BIN_TARGET="`pwd`/doctrine" +cd "$SRC_DIR" +"$BIN_TARGET" "$@" diff --git a/vendor/bin/doctrine-module b/vendor/bin/doctrine-module new file mode 100644 index 00000000..c101fb57 --- /dev/null +++ b/vendor/bin/doctrine-module @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +SRC_DIR="`pwd`" +cd "`dirname "$0"`" +cd "../doctrine/doctrine-module/bin" +BIN_TARGET="`pwd`/doctrine-module" +cd "$SRC_DIR" +"$BIN_TARGET" "$@" diff --git a/vendor/bin/doctrine-module.bat b/vendor/bin/doctrine-module.bat new file mode 100644 index 00000000..e785bb61 --- /dev/null +++ b/vendor/bin/doctrine-module.bat @@ -0,0 +1,3 @@ +@ECHO OFF +SET BIN_TARGET=%~dp0\"../doctrine/doctrine-module/bin"\doctrine-module +php "%BIN_TARGET%" %* diff --git a/vendor/bin/doctrine.bat b/vendor/bin/doctrine.bat new file mode 100644 index 00000000..d2a342d2 --- /dev/null +++ b/vendor/bin/doctrine.bat @@ -0,0 +1,3 @@ +@ECHO OFF +SET BIN_TARGET=%~dp0\"../doctrine/orm/bin"\doctrine +php "%BIN_TARGET%" %* diff --git a/vendor/bin/doctrine.php b/vendor/bin/doctrine.php new file mode 100644 index 00000000..ff36856e --- /dev/null +++ b/vendor/bin/doctrine.php @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +SRC_DIR="`pwd`" +cd "`dirname "$0"`" +cd "../doctrine/orm/bin" +BIN_TARGET="`pwd`/doctrine.php" +cd "$SRC_DIR" +"$BIN_TARGET" "$@" diff --git a/vendor/bin/doctrine.php.bat b/vendor/bin/doctrine.php.bat new file mode 100644 index 00000000..40d86089 --- /dev/null +++ b/vendor/bin/doctrine.php.bat @@ -0,0 +1,3 @@ +@ECHO OFF +SET BIN_TARGET=%~dp0\"../doctrine/orm/bin"\doctrine.php +php "%BIN_TARGET%" %* diff --git a/vendor/doctrine/annotations/.gitignore b/vendor/doctrine/annotations/.gitignore new file mode 100644 index 00000000..48b8bf90 --- /dev/null +++ b/vendor/doctrine/annotations/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/vendor/doctrine/annotations/.travis.yml b/vendor/doctrine/annotations/.travis.yml new file mode 100644 index 00000000..478e5d65 --- /dev/null +++ b/vendor/doctrine/annotations/.travis.yml @@ -0,0 +1,8 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - composer --prefer-source --dev install diff --git a/vendor/doctrine/annotations/README.md b/vendor/doctrine/annotations/README.md new file mode 100644 index 00000000..1107dc8e --- /dev/null +++ b/vendor/doctrine/annotations/README.md @@ -0,0 +1,9 @@ +# Doctrine Annotations + +Docblock Annotations Parser library (extracted from Doctrine Common). + +## Changelog + +### v1.1 + +* Add Exception when ZendOptimizer+ or Opcache is configured to drop comments diff --git a/vendor/doctrine/annotations/composer.json b/vendor/doctrine/annotations/composer.json new file mode 100644 index 00000000..3569cf4c --- /dev/null +++ b/vendor/doctrine/annotations/composer.json @@ -0,0 +1,30 @@ +{ + "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": ">=5.3.2", + "doctrine/lexer": "1.*" + }, + "require-dev": { + "doctrine/cache": "1.*" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Annotations\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.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 00000000..6a1390af --- /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 Unkown 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 00000000..dbef6df0 --- /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 00000000..53134e30 --- /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 00000000..315812f5 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php @@ -0,0 +1,85 @@ +. + */ + +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 construct + * + * @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']; + } + +} \ No newline at end of file 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 00000000..a84a4f51 --- /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 00000000..d67f9606 --- /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 00000000..64655ef6 --- /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 construct + * + * @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 00000000..6cdb6615 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,158 @@ +. + */ + +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 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 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 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 typeError($attributeName, $annotationName, $context, $expected, $actual) + { + return new self(sprintf( + '[Type Error] 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 new self(sprintf( + '[Type Error] 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) + { + throw 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() + { + throw new self("You have to enable opcache.save_comments=1 or zend_optimizerplus.save_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 00000000..ad4a2649 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,318 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +use Closure; +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( + 'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true, + 'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true, + 'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true, + 'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true, + 'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true, + 'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true, + 'inheritDoc'=> true, 'license'=> true, 'todo'=> true, 'TODO'=> true, + 'deprec'=> true, 'property' => true, 'method' => true, + 'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true, + 'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true, + 'usedby'=> true, 'private' => true, 'Annotation' => true, 'override' => true, + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + 'Required' => true, 'Attribute' => true, 'Attributes' => true, + 'Target' => true, 'SuppressWarnings' => true, + 'ingroup' => true, 'code' => true, 'endcode' => true, + 'package_version' => true, 'fixme' => true + ); + + /** + * 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; + } + + /** + * 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. + */ + public function __construct() + { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (extension_loaded('opcache') && ini_get('opcache.save_comments') == 0) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); + + $this->parser = new DocParser; + + $this->preParser = new DocParser; + $this->preParser->setImports(self::$globalImports); + $this->preParser->setIgnoreNotImportedAnnotations(true); + + $this->phpParser = new PhpParser; + } + + /** + * 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. + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * 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 mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * 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. + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * Gets a property annotation. + * + * @param ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * 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. + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * Gets a method annotation. + * + * @param ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + 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) + { + if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) { + return $this->ignoredAnnotationNames[$name]; + } + $this->collectParsingMetadata($class); + + return $this->ignoredAnnotationNames[$name]; + } + + /** + * Retrieve imports + * + * @param \ReflectionClass $class + * @return array + */ + private function getImports(ReflectionClass $class) + { + if (isset($this->imports[$name = $class->getName()])) { + return $this->imports[$name]; + } + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * 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 00000000..6135f53d --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,139 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * AnnotationRegistry + */ +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 array + */ + static private $autoloadNamespaces = array(); + + /** + * A map of autoloader callables. + * + * @var array + */ + static private $loaders = array(); + + static public function reset() + { + self::$autoloadNamespaces = array(); + self::$loaders = array(); + } + + /** + * Register file + * + * @param string $file + */ + static public function registerFile($file) + { + require_once $file; + } + + /** + * Add 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 + */ + static public function registerAutoloadNamespace($namespace, $dirs = null) + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Register multiple namespaces + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param array $namespaces + */ + static public function registerAutoloadNamespaces(array $namespaces) + { + self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Register 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. + * + * @param callable $callable + * + * @throws \InvalidArgumentException + */ + static public function registerLoader($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader()."); + } + self::$loaders[] = $callable; + } + + /** + * Autoload an annotation class silently. + * + * @param string $class + * @return boolean + */ + static public function loadAnnotationClass($class) + { + 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 (call_user_func($loader, $class) === true) { + return true; + } + } + 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 00000000..e377e3b3 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Cache\Cache; + +/** + * A cache aware annotation reader. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +final class CachedReader implements Reader +{ + /** + * @var string + */ + private static $CACHE_SALT = '@[Annot]'; + + /** + * @var Reader + */ + private $delegate; + + /** + * @var Cache + */ + private $cache; + + /** + * @var boolean + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations; + + /** + * 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; + } + + /** + * Get annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + 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; + } + + /** + * Get selected annotation for class + * + * @param \ReflectionClass $class + * @param string $annotationName + * @return null + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + 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; + } + + /** + * Get selected annotation for property + * + * @param \ReflectionProperty $property + * @param string $annotationName + * @return null + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Get method annotations + * + * @param \ReflectionMethod $method + * @return array + */ + 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; + } + + /** + * Get selected method annotation + * + * @param \ReflectionMethod $method + * @param string $annotationName + * @return null + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Clear loaded annotations + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } + + /** + * Fetches a value from the cache. + * + * @param string $rawCacheKey The cache key. + * @param \ReflectionClass $class The related class. + * @return mixed|boolean The cached value or false when the value is not in cache. + */ + private function fetchFromCache($rawCacheKey, \ReflectionClass $class) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + 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 $rawCacheKey The cache key. + * @param mixed $value The value. + */ + private function saveToCache($rawCacheKey, $value) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + $this->cache->save($cacheKey, $value); + if ($this->debug) { + $this->cache->save('[C]'.$cacheKey, time()); + } + } + + /** + * Check if cache is fresh + * + * @param string $cacheKey + * @param \ReflectionClass $class + * @return bool + */ + private function isCacheFresh($cacheKey, \ReflectionClass $class) + { + if (false === $filename = $class->getFilename()) { + return true; + } + + return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename); + } +} 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 00000000..ddc84d69 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,132 @@ +. + */ + +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; + + 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 + ); + + 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]{1}', + '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', + '"(?:[^"]|"")*"', + ); + } + + /** + * {@inheritdoc} + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '\*+', '(.)'); + } + + /** + * {@inheritdoc} + * + * @param string $value + * + * @return int + */ + 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 00000000..5896e237 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,1041 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Closure; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Enum; +use Doctrine\Common\Annotations\Annotation\Target; +use Doctrine\Common\Annotations\Annotation\Attribute; +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 string + */ + 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 array + */ + 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 array + */ + private $ignoredAnnotationNames = 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 array $names + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + /** + * Sets ignore on not-imported annotations + * + * @param $bool + */ + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (Boolean) $bool; + } + + /** + * Sets the default namespaces. + * + * @param array $namespace + * + * @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 + * @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 + */ + 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 = '') + { + if (false === $pos = strpos($input, '@')) { + return array(); + } + + // also parse whatever character is before the @ + if ($pos > 0) { + $pos -= 1; + } + + $this->context = $context; + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * Attempts to match the given token with the current lookahead token. + * If they match, updates the lookahead token; otherwise raises a syntax error. + * + * @param int $token type of Token. + * @return bool 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 bool + */ + 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 $token Optional token. + * + * @throws AnnotationException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = "Expected {$expected}, got "; + + if ($this->lexer->lookahead === null) { + $message .= 'end of string'; + } else { + $message .= "'{$token['value']}' at position {$token['position']}"; + } + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempt 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 + */ + 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; + + } elseif ($annotation instanceof Attributes) { + foreach ($annotation->value as $attrib) { + // handle internal type declaration + $type = isset(self::$typeMap[$attrib->type]) ? self::$typeMap[$attrib->type] : $attrib->type; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has array + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attrib->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$attrib->name]['type'] = $type; + $metadata['attribute_types'][$attrib->name]['value'] = $attrib->type; + $metadata['attribute_types'][$attrib->name]['required'] = $attrib->required; + } + } + } + } + + // 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; + } + + // checks if the property has @var annotation + if (false !== strpos($propertyComment, '@var') + && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) { + // literal type declaration + $value = $matches[1]; + + // handle internal type declaration + $type = isset(self::$typeMap[$value]) ? self::$typeMap[$value] : $value; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has @var array annotation + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$property->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$property->name]['type'] = $type; + $metadata['attribute_types'][$property->name]['value'] = $value; + $metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required'); + } + } + + // 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) { + $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; + } + + /** + * 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 ["(" [Values] ")"] + * AnnotationName ::= QualifiedName | SimpleName + * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName + * NameSpacePart ::= identifier | null | false | true + * SimpleName ::= identifier | null | false | true + * + * @throws AnnotationException + * @return mixed False if it is not a valid annotation. + */ + 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]) { + $alias = (false === $pos = strpos($name, '\\'))? $name : substr($name, 0, $pos); + + $found = false; + if ($this->namespaces) { + foreach ($this->namespaces as $namespace) { + if ($this->classExists($namespace.'\\'.$name)) { + $name = $namespace.'\\'.$name; + $found = true; + break; + } + } + } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) { + if (false !== $pos) { + $name = $this->imports[$loweredAlias].substr($name, $pos); + } else { + $name = $this->imports[$loweredAlias]; + } + $found = true; + } elseif (isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'].'\\'.$name)) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif ($this->classExists($name)) { + $found = true; + } + + if (!$found) { + if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$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)); + } + } + + 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 (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 = array(); + if ($this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + } + + 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::typeError($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::typeError($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; + } + + /** + * Values ::= Array | Value {"," Value}* + * + * @return array + */ + private function Values() + { + $values = array(); + + // Handle the case of a single array as value, i.e. @Foo({....}) + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + $values['value'] = $this->Value(); + return $values; + } + + $values[] = $this->Value(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + $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 + * + * @throws AnnotationException + * @return mixed + */ + private function Constant() + { + $identifier = $this->Identifier(); + + if (!defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) { + + list($className, $const) = explode('::', $identifier); + $alias = (false === $pos = strpos($className, '\\'))? $className : substr($className, 0, $pos); + + $found = false; + 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 = strtolower($alias)]): + $found = true; + if (false !== $pos) { + $className = $this->imports[$loweredAlias].substr($className, $pos); + } else { + $className = $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; + } + } + + 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->lexer->moveNext(); + $className = $this->lexer->token['value']; + } else { + $this->syntaxError('namespace separator or identifier'); + } + + 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 array + */ + 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); + $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()); + } +} 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 00000000..5e937a86 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,269 @@ +. + */ + +namespace Doctrine\Common\Annotations; + + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + + /** + * @var string + */ + private $dir; + + /** + * @var bool + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + private $classNameHashes = array(); + + /** + * Constructor + * + * @param Reader $reader + * @param string $cacheDir + * @param bool $debug + * + * @throws \InvalidArgumentException + */ + public function __construct(Reader $reader, $cacheDir, $debug = false) + { + $this->reader = $reader; + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + if (!is_writable($cacheDir)) { + 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.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + /** + * Retrieve annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + 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; + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + 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; + } + + /** + * Retrieve annotations for method + * + * @param \ReflectionMethod $method + * @return array + */ + 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; + } + + /** + * Save cache file + * + * @param string $path + * @param mixed $data + */ + private function saveCacheFile($path, $data) + { + file_put_contents($path, 'getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Clear stores annotations + */ + 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 00000000..2dfdd4da --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Reader; + +/** + * 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; + } + + /** + * Get Annotations for class + * + * @param \ReflectionClass $class + * @return array + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for class + * + * @param \ReflectionClass $class + * @param string $annotation + * @return mixed + */ + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + /** + * Get Annotations for method + * + * @param \ReflectionMethod $method + * @return array + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for method + * + * @param \ReflectionMethod $method + * @param string $annotation + * @return mixed + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + /** + * Get annotations for property + * + * @param \ReflectionProperty $property + * @return array + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * Get selected annotation for property + * + * @param \ReflectionProperty $property + * @param string $annotation + * @return mixed + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxy 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 00000000..9d61020d --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,89 @@ +. + */ + +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; + } + + /** + * Get the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param int $lineNumber The number of lines to read from file. + * @return string The content of the file. + */ + 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 00000000..6a01cb4a --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + /** + * @param \ReflectionClass $class + * @return mixed + */ + function getClassAnnotations(\ReflectionClass $class); + + /** + * @param \ReflectionClass $class + * @param string $annotationName + * @return mixed + */ + function getClassAnnotation(\ReflectionClass $class, $annotationName); + + /** + * @param \ReflectionMethod $method + * @return mixed + */ + function getMethodAnnotations(\ReflectionMethod $method); + + /** + * @param \ReflectionMethod $method + * @param string $annotationName + * @return mixed + */ + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + + /** + * @param \ReflectionProperty $property + * @return mixed + */ + function getPropertyAnnotations(\ReflectionProperty $property); + + /** + * @param \ReflectionProperty $property + * @param string $annotationName + * @return mixed + */ + 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 00000000..4210d901 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,157 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Target; + +/** + * 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 + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * 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. + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * 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. + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * 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. + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * 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 mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return mixed The Annotation or NULL, if the requested annotation does not exist. + */ + 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 00000000..a1ef1154 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php @@ -0,0 +1,175 @@ +. + */ + +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 = 0; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + public function __construct($contents) + { + $this->tokens = token_get_all($contents); + $this->numTokens = count($this->tokens); + $this->pointer = 0; + } + + /** + * Gets the next non whitespace and non comment token. + * + * @param $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 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; + } + + /** + * Parse a single use statement. + * + * @return array A list with all found class names for a use statement. + */ + public function parseUseStatement() + { + $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)] = $class; + $class = ''; + $alias = ''; + $explicitAlias = false; + } else if ($token === ';') { + $statements[strtolower($alias)] = $class; + break; + } else { + break; + } + } + + return $statements; + } + + /** + * Get 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; + } + + /** + * Get 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; + } + + /** + * Get the class name. + * + * @return string The foundclass 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/phpunit.xml.dist b/vendor/doctrine/annotations/phpunit.xml.dist new file mode 100644 index 00000000..6ab0c8c8 --- /dev/null +++ b/vendor/doctrine/annotations/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php new file mode 100644 index 00000000..ea52b02e --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php @@ -0,0 +1,571 @@ +getReflectionClass(); + $reader = $this->getReader(); + + $this->assertEquals(1, count($reader->getClassAnnotations($class))); + $this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyAnnotation', $annot = $reader->getClassAnnotation($class, $annotName)); + $this->assertEquals("hello", $annot->dummyValue); + + $field1Prop = $class->getProperty('field1'); + $propAnnots = $reader->getPropertyAnnotations($field1Prop); + $this->assertEquals(1, count($propAnnots)); + $this->assertInstanceOf($annotName, $annot = $reader->getPropertyAnnotation($field1Prop, $annotName)); + $this->assertEquals("fieldHello", $annot->dummyValue); + + $getField1Method = $class->getMethod('getField1'); + $methodAnnots = $reader->getMethodAnnotations($getField1Method); + $this->assertEquals(1, count($methodAnnots)); + $this->assertInstanceOf($annotName, $annot = $reader->getMethodAnnotation($getField1Method, $annotName)); + $this->assertEquals(array(1, 2, "three"), $annot->value); + + $field2Prop = $class->getProperty('field2'); + $propAnnots = $reader->getPropertyAnnotations($field2Prop); + $this->assertEquals(1, count($propAnnots)); + $this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyJoinTable', $joinTableAnnot = $reader->getPropertyAnnotation($field2Prop, $annotName)); + $this->assertEquals(1, count($joinTableAnnot->joinColumns)); + $this->assertEquals(1, count($joinTableAnnot->inverseJoinColumns)); + $this->assertTrue($joinTableAnnot->joinColumns[0] instanceof DummyJoinColumn); + $this->assertTrue($joinTableAnnot->inverseJoinColumns[0] instanceof DummyJoinColumn); + $this->assertEquals('col1', $joinTableAnnot->joinColumns[0]->name); + $this->assertEquals('col2', $joinTableAnnot->joinColumns[0]->referencedColumnName); + $this->assertEquals('col3', $joinTableAnnot->inverseJoinColumns[0]->name); + $this->assertEquals('col4', $joinTableAnnot->inverseJoinColumns[0]->referencedColumnName); + + $dummyAnnot = $reader->getMethodAnnotation($class->getMethod('getField1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation'); + $this->assertEquals('', $dummyAnnot->dummyValue); + $this->assertEquals(array(1, 2, 'three'), $dummyAnnot->value); + + $dummyAnnot = $reader->getPropertyAnnotation($class->getProperty('field1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation'); + $this->assertEquals('fieldHello', $dummyAnnot->dummyValue); + + $classAnnot = $reader->getClassAnnotation($class, 'Doctrine\Tests\Common\Annotations\DummyAnnotation'); + $this->assertEquals('hello', $classAnnot->dummyValue); + } + + public function testAnnotationsWithValidTargets() + { + $reader = $this->getReader(); + $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithValidAnnotationTarget'); + + $this->assertEquals(1,count($reader->getClassAnnotations($class))); + $this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('foo')))); + $this->assertEquals(1,count($reader->getMethodAnnotations($class->getMethod('someFunction')))); + $this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('nested')))); + } + + public function testAnnotationsWithVarType() + { + $reader = $this->getReader(); + $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType'); + + $this->assertEquals(1,count($fooAnnot = $reader->getPropertyAnnotations($class->getProperty('foo')))); + $this->assertEquals(1,count($barAnnot = $reader->getMethodAnnotations($class->getMethod('bar')))); + + $this->assertInternalType('string', $fooAnnot[0]->string); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', $barAnnot[0]->annotation); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetPropertyMethod is not allowed to be declared on class Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass. You may only use this annotation on these code elements: METHOD, PROPERTY + */ + public function testClassWithInvalidAnnotationTargetAtClassDocBlock() + { + $reader = $this->getReader(); + $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass')); + } + + public function testClassWithWithInclude() + { + $reader = $this->getReader(); + $annots = $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithRequire')); + $this->assertCount(1, $annots); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$foo. You may only use this annotation on these code elements: CLASS + */ + public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'foo')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetAnnotation is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$bar. You may only use this annotation on these code elements: ANNOTATION + */ + public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'bar')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod::functionName(). You may only use this annotation on these code elements: CLASS + */ + public function testClassWithInvalidAnnotationTargetAtMethodDocBlock() + { + $reader = $this->getReader(); + $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod', 'functionName')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError. + */ + public function testClassWithAnnotationWithTargetSyntaxErrorAtClassDocBlock() + { + $reader = $this->getReader(); + $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError. + */ + public function testClassWithAnnotationWithTargetSyntaxErrorAtPropertyDocBlock() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','foo')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError. + */ + public function testClassWithAnnotationWithTargetSyntaxErrorAtMethodDocBlock() + { + $reader = $this->getReader(); + $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','bar')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Type Error] Attribute "string" of @AnnotationWithVarType declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::$invalidProperty expects a(n) string, but got integer. + */ + public function testClassWithPropertyInvalidVarTypeError() + { + $reader = $this->getReader(); + $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType'); + + $reader->getPropertyAnnotations($class->getProperty('invalidProperty')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Type Error] Attribute "annotation" of @AnnotationWithVarType declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::invalidMethod() expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll, but got an instance of Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation. + */ + public function testClassWithMethodInvalidVarTypeError() + { + $reader = $this->getReader(); + $class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType'); + + $reader->getMethodAnnotations($class->getMethod('invalidMethod')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in class Doctrine\Tests\Common\Annotations\DummyClassSyntaxError. + */ + public function testClassSyntaxErrorContext() + { + $reader = $this->getReader(); + $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\DummyClassSyntaxError')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in method Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError::foo(). + */ + public function testMethodSyntaxErrorContext() + { + $reader = $this->getReader(); + $reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError', 'foo')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in property Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError::$foo. + */ + public function testPropertySyntaxErrorContext() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError', 'foo')); + } + + /** + * @group regression + */ + public function testMultipleAnnotationsOnSameLine() + { + $reader = $this->getReader(); + $annots = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClass2', 'id')); + $this->assertEquals(3, count($annots)); + } + + public function testNonAnnotationProblem() + { + $reader = $this->getReader(); + + $this->assertNotNull($annot = $reader->getPropertyAnnotation(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassNonAnnotationProblem', 'foo'), $name = 'Doctrine\Tests\Common\Annotations\DummyAnnotation')); + $this->assertInstanceOf($name, $annot); + } + + public function testImportWithConcreteAnnotation() + { + $reader = $this->getReader(); + $property = new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestImportWithConcreteAnnotation', 'field'); + $annotations = $reader->getPropertyAnnotations($property); + $this->assertEquals(1, count($annotations)); + $this->assertNotNull($reader->getPropertyAnnotation($property, 'Doctrine\Tests\Common\Annotations\DummyAnnotation')); + } + + public function testImportWithInheritance() + { + $reader = $this->getReader(); + + $class = new TestParentClass(); + $ref = new \ReflectionClass($class); + + $childAnnotations = $reader->getPropertyAnnotations($ref->getProperty('child')); + $this->assertEquals(1, count($childAnnotations)); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Foo\Name', reset($childAnnotations)); + + $parentAnnotations = $reader->getPropertyAnnotations($ref->getProperty('parent')); + $this->assertEquals(1, count($parentAnnotations)); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Bar\Name', reset($parentAnnotations)); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage The annotation "@NameFoo" in property Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass::$field was never imported. + */ + public function testImportDetectsNotImportedAnnotation() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass', 'field')); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage The annotation "@Foo\Bar\Name" in property Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass::$field was never imported. + */ + public function testImportDetectsNonExistentAnnotation() + { + $reader = $this->getReader(); + $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass', 'field')); + } + + public function testTopLevelAnnotation() + { + $reader = $this->getReader(); + $annotations = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestTopLevelAnnotationClass', 'field')); + + $this->assertEquals(1, count($annotations)); + $this->assertInstanceOf('\TopLevelAnnotation', reset($annotations)); + } + + public function testIgnoresAnnotationsNotPrefixedWithWhitespace() + { + $reader = $this->getReader(); + + $annotation = $reader->getClassAnnotation(new \ReflectionClass(new TestIgnoresNonAnnotationsClass()), 'Doctrine\Tests\Common\Annotations\Name'); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Name', $annotation); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage The class "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation" 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 "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation". If it is indeed no annotation, then you need to add @IgnoreAnnotation("NoAnnotation") to the _class_ doc comment of class Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass. + */ + public function testErrorWhenInvalidAnnotationIsUsed() + { + $reader = $this->getReader(); + $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass'); + $reader->getClassAnnotations($ref); + } + + public function testInvalidAnnotationUsageButIgnoredClass() + { + $reader = $this->getReader(); + $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageButIgnoredClass'); + $annots = $reader->getClassAnnotations($ref); + + $this->assertEquals(2, count($annots)); + } + + /** + * @group DDC-1660 + * @group regression + */ + public function testInvalidAnnotationButIgnored() + { + $reader = $this->getReader(); + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660'); + + $this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version')); + $this->assertCount(0, $reader->getClassAnnotations($class)); + $this->assertCount(0, $reader->getMethodAnnotations($class->getMethod('bar'))); + $this->assertCount(0, $reader->getPropertyAnnotations($class->getProperty('foo'))); + } + + public function testAnnotationEnumeratorException() + { + $reader = $this->getReader(); + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum'); + + $this->assertCount(1, $bar = $reader->getMethodAnnotations($class->getMethod('bar'))); + $this->assertCount(1, $foo = $reader->getPropertyAnnotations($class->getProperty('foo'))); + + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $bar[0]); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $foo[0]); + + try { + $reader->getPropertyAnnotations($class->getProperty('invalidProperty')); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::$invalidProperty accept only [ONE, TWO, THREE], but got FOUR.', $exc->getMessage()); + } + + try { + $reader->getMethodAnnotations($class->getMethod('invalidMethod')); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::invalidMethod() accept only [ONE, TWO, THREE], but got 5.', $exc->getMessage()); + } + } + + /** + * @group DCOM-106 + */ + public function testIgnoreFixMeAndUpperCaseToDo() + { + $reader = $this->getReader(); + $ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\DCOM106'); + $reader->getClassAnnotations($ref); + } + + /** + * @return AnnotationReader + */ + abstract protected function getReader(); +} + +/** + * @parseAnnotation("var") + * @author Johannes M. Schmitt + * + */ +class TestParseAnnotationClass +{ + /** + * @var + */ + private $field; +} + +/** + * @Name + * @author Johannes M. Schmitt + */ +class TestIgnoresNonAnnotationsClass +{ +} + +class TestTopLevelAnnotationClass +{ + /** + * @\TopLevelAnnotation + */ + private $field; +} + +class TestNonExistentAnnotationClass +{ + /** + * @Foo\Bar\Name + */ + private $field; +} + +class TestAnnotationNotImportedClass +{ + /** + * @NameFoo + */ + private $field; +} + +class TestChildClass +{ + /** + * @\Doctrine\Tests\Common\Annotations\Foo\Name(name = "foo") + */ + protected $child; +} + +class TestParentClass extends TestChildClass +{ + /** + * @\Doctrine\Tests\Common\Annotations\Bar\Name(name = "bar") + */ + private $parent; +} + +class TestImportWithConcreteAnnotation +{ + /** + * @DummyAnnotation(dummyValue = "bar") + */ + private $field; +} + +/** + * @ignoreAnnotation("var") + */ +class DummyClass2 { + /** + * @DummyId @DummyColumn(type="integer") @DummyGeneratedValue + * @var integer + */ + private $id; +} + +/** @Annotation */ +class DummyId extends \Doctrine\Common\Annotations\Annotation {} +/** @Annotation */ +class DummyColumn extends \Doctrine\Common\Annotations\Annotation { + public $type; +} +/** @Annotation */ +class DummyGeneratedValue extends \Doctrine\Common\Annotations\Annotation {} +/** @Annotation */ +class DummyAnnotation extends \Doctrine\Common\Annotations\Annotation { + public $dummyValue; +} + +/** + * @api + * @Annotation + */ +class DummyAnnotationWithIgnoredAnnotation extends \Doctrine\Common\Annotations\Annotation { + public $dummyValue; +} + +/** @Annotation */ +class DummyJoinColumn extends \Doctrine\Common\Annotations\Annotation { + public $name; + public $referencedColumnName; +} +/** @Annotation */ +class DummyJoinTable extends \Doctrine\Common\Annotations\Annotation { + public $name; + public $joinColumns; + public $inverseJoinColumns; +} + +/** + * @DummyAnnotation(@) + */ +class DummyClassSyntaxError +{ + +} + +class DummyClassMethodSyntaxError +{ + /** + * @DummyAnnotation(@) + */ + public function foo() + { + + } +} + +class DummyClassPropertySyntaxError +{ + /** + * @DummyAnnotation(@) + */ + public $foo; +} + +/** + * @ignoreAnnotation({"since", "var"}) + */ +class DummyClassNonAnnotationProblem +{ + /** + * @DummyAnnotation + * + * @var \Test + * @since 0.1 + */ + public $foo; +} + + +/** +* @DummyAnnotation Foo bar +*/ +class DummyClassWithEmail +{ + +} + + +/** + * @fixme public + * @TODO + */ +class DCOM106 +{ + +} + +namespace Doctrine\Tests\Common\Annotations\Foo; + +/** @Annotation */ +class Name extends \Doctrine\Common\Annotations\Annotation +{ + public $name; +} + +namespace Doctrine\Tests\Common\Annotations\Bar; + +/** @Annotation */ +class Name extends \Doctrine\Common\Annotations\Annotation +{ + public $name; +} diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php new file mode 100644 index 00000000..d2cc6678 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationReaderTest.php @@ -0,0 +1,13 @@ +getMock('Doctrine\Common\Cache\Cache'); + $cache + ->expects($this->at(0)) + ->method('fetch') + ->with($this->equalTo($cacheKey)) + ->will($this->returnValue(array())) + ; + $cache + ->expects($this->at(1)) + ->method('fetch') + ->with($this->equalTo('[C]'.$cacheKey)) + ->will($this->returnValue(time() - 10)) + ; + $cache + ->expects($this->at(2)) + ->method('save') + ->with($this->equalTo($cacheKey)) + ; + $cache + ->expects($this->at(3)) + ->method('save') + ->with($this->equalTo('[C]'.$cacheKey)) + ; + + $reader = new CachedReader(new AnnotationReader(), $cache, true); + $route = new Route(); + $route->pattern = '/someprefix'; + $this->assertEquals(array($route), $reader->getClassAnnotations(new \ReflectionClass($name))); + } + + protected function getReader() + { + $this->cache = new ArrayCache(); + return new CachedReader(new AnnotationReader(), $this->cache); + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php new file mode 100644 index 00000000..03a55c80 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocLexerTest.php @@ -0,0 +1,137 @@ +setInput("@Name"); + $this->assertNull($lexer->token); + $this->assertNull($lexer->lookahead); + + $this->assertTrue($lexer->moveNext()); + $this->assertNull($lexer->token); + $this->assertEquals('@', $lexer->lookahead['value']); + + $this->assertTrue($lexer->moveNext()); + $this->assertEquals('@', $lexer->token['value']); + $this->assertEquals('Name', $lexer->lookahead['value']); + + $this->assertFalse($lexer->moveNext()); + } + + public function testScannerTokenizesDocBlockWhitConstants() + { + $lexer = new DocLexer(); + $docblock = '@AnnotationWithConstants(PHP_EOL, ClassWithConstants::SOME_VALUE, \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)'; + + $tokens = array ( + array( + 'value' => '@', + 'position' => 0, + 'type' => DocLexer::T_AT, + ), + array( + 'value' => 'AnnotationWithConstants', + 'position' => 1, + 'type' => DocLexer::T_IDENTIFIER, + ), + array( + 'value' => '(', + 'position' => 24, + 'type' => DocLexer::T_OPEN_PARENTHESIS, + ), + array( + 'value' => 'PHP_EOL', + 'position' => 25, + 'type' => DocLexer::T_IDENTIFIER, + ), + array( + 'value' => ',', + 'position' => 32, + 'type' => DocLexer::T_COMMA, + ), + array( + 'value' => 'ClassWithConstants::SOME_VALUE', + 'position' => 34, + 'type' => DocLexer::T_IDENTIFIER, + ), + array( + 'value' => ',', + 'position' => 64, + 'type' => DocLexer::T_COMMA, + ), + array( + 'value' => '\\Doctrine\\Tests\\Common\\Annotations\\Fixtures\\IntefaceWithConstants::SOME_VALUE', + 'position' => 66, + 'type' => DocLexer::T_IDENTIFIER, + ), + array( + 'value' => ')', + 'position' => 143, + 'type' => DocLexer::T_CLOSE_PARENTHESIS, + ) + + ); + + $lexer->setInput($docblock); + + foreach ($tokens as $expected) { + $lexer->moveNext(); + $lookahead = $lexer->lookahead; + $this->assertEquals($expected['value'], $lookahead['value']); + $this->assertEquals($expected['type'], $lookahead['type']); + $this->assertEquals($expected['position'], $lookahead['position']); + } + + $this->assertFalse($lexer->moveNext()); + } + + + public function testScannerTokenizesDocBlockWhitInvalidIdentifier() + { + $lexer = new DocLexer(); + $docblock = '@Foo\3.42'; + + $tokens = array ( + array( + 'value' => '@', + 'position' => 0, + 'type' => DocLexer::T_AT, + ), + array( + 'value' => 'Foo', + 'position' => 1, + 'type' => DocLexer::T_IDENTIFIER, + ), + array( + 'value' => '\\', + 'position' => 4, + 'type' => DocLexer::T_NAMESPACE_SEPARATOR, + ), + array( + 'value' => 3.42, + 'position' => 5, + 'type' => DocLexer::T_FLOAT, + ) + ); + + $lexer->setInput($docblock); + + foreach ($tokens as $expected) { + $lexer->moveNext(); + $lookahead = $lexer->lookahead; + $this->assertEquals($expected['value'], $lookahead['value']); + $this->assertEquals($expected['type'], $lookahead['type']); + $this->assertEquals($expected['position'], $lookahead['position']); + } + + $this->assertFalse($lexer->moveNext()); + } + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php new file mode 100644 index 00000000..86a36953 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php @@ -0,0 +1,1264 @@ +createTestParser(); + + // Nested arrays with nested annotations + $result = $parser->parse('@Name(foo={1,2, {"key"=@Name}})'); + $annot = $result[0]; + + $this->assertTrue($annot instanceof Name); + $this->assertNull($annot->value); + $this->assertEquals(3, count($annot->foo)); + $this->assertEquals(1, $annot->foo[0]); + $this->assertEquals(2, $annot->foo[1]); + $this->assertTrue(is_array($annot->foo[2])); + + $nestedArray = $annot->foo[2]; + $this->assertTrue(isset($nestedArray['key'])); + $this->assertTrue($nestedArray['key'] instanceof Name); + } + + public function testBasicAnnotations() + { + $parser = $this->createTestParser(); + + // Marker annotation + $result = $parser->parse("@Name"); + $annot = $result[0]; + $this->assertTrue($annot instanceof Name); + $this->assertNull($annot->value); + $this->assertNull($annot->foo); + + // Associative arrays + $result = $parser->parse('@Name(foo={"key1" = "value1"})'); + $annot = $result[0]; + $this->assertNull($annot->value); + $this->assertTrue(is_array($annot->foo)); + $this->assertTrue(isset($annot->foo['key1'])); + + // Numerical arrays + $result = $parser->parse('@Name({2="foo", 4="bar"})'); + $annot = $result[0]; + $this->assertTrue(is_array($annot->value)); + $this->assertEquals('foo', $annot->value[2]); + $this->assertEquals('bar', $annot->value[4]); + $this->assertFalse(isset($annot->value[0])); + $this->assertFalse(isset($annot->value[1])); + $this->assertFalse(isset($annot->value[3])); + + // Multiple values + $result = $parser->parse('@Name(@Name, @Name)'); + $annot = $result[0]; + + $this->assertTrue($annot instanceof Name); + $this->assertTrue(is_array($annot->value)); + $this->assertTrue($annot->value[0] instanceof Name); + $this->assertTrue($annot->value[1] instanceof Name); + + // Multiple types as values + $result = $parser->parse('@Name(foo="Bar", @Name, {"key1"="value1", "key2"="value2"})'); + $annot = $result[0]; + + $this->assertTrue($annot instanceof Name); + $this->assertTrue(is_array($annot->value)); + $this->assertTrue($annot->value[0] instanceof Name); + $this->assertTrue(is_array($annot->value[1])); + $this->assertEquals('value1', $annot->value[1]['key1']); + $this->assertEquals('value2', $annot->value[1]['key2']); + + // Complete docblock + $docblock = <<parse($docblock); + $this->assertEquals(1, count($result)); + $annot = $result[0]; + $this->assertTrue($annot instanceof Name); + $this->assertEquals("bar", $annot->foo); + $this->assertNull($annot->value); + } + + public function testNamespacedAnnotations() + { + $parser = new DocParser; + $parser->setIgnoreNotImportedAnnotations(true); + + $docblock = << + * @Doctrine\Tests\Common\Annotations\Name(foo="bar") + * @ignore + */ +DOCBLOCK; + + $result = $parser->parse($docblock); + $this->assertEquals(1, count($result)); + $annot = $result[0]; + $this->assertTrue($annot instanceof Name); + $this->assertEquals("bar", $annot->foo); + } + + /** + * @group debug + */ + public function testTypicalMethodDocBlock() + { + $parser = $this->createTestParser(); + + $docblock = <<parse($docblock); + $this->assertEquals(2, count($result)); + $this->assertTrue(isset($result[0])); + $this->assertTrue(isset($result[1])); + $annot = $result[0]; + $this->assertTrue($annot instanceof Name); + $this->assertEquals("bar", $annot->foo); + $marker = $result[1]; + $this->assertTrue($marker instanceof Marker); + } + + + public function testAnnotationWithoutConstructor() + { + $parser = $this->createTestParser(); + + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertNotNull($annot); + $this->assertTrue($annot instanceof SomeAnnotationClassNameWithoutConstructor); + + $this->assertNull($annot->name); + $this->assertNotNull($annot->data); + $this->assertEquals($annot->data, "Some data"); + + + + +$docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertNotNull($annot); + $this->assertTrue($annot instanceof SomeAnnotationClassNameWithoutConstructor); + + $this->assertEquals($annot->name, "Some Name"); + $this->assertEquals($annot->data, "Some data"); + + + + +$docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertEquals($annot->data, "Some data"); + $this->assertNull($annot->name); + + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertEquals($annot->name, "Some name"); + $this->assertNull($annot->data); + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertEquals($annot->data, "Some data"); + $this->assertNull($annot->name); + + + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertEquals($annot->name, "Some name"); + $this->assertEquals($annot->data, "Some data"); + + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $annot = $result[0]; + + $this->assertEquals($annot->name, "Some name"); + $this->assertEquals($annot->data, "Some data"); + + $docblock = <<parse($docblock); + $this->assertEquals(count($result), 1); + $this->assertTrue($result[0] instanceof SomeAnnotationClassNameWithoutConstructorAndProperties); + } + + public function testAnnotationTarget() + { + + $parser = new DocParser; + $parser->setImports(array( + '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations\Fixtures', + )); + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithValidAnnotationTarget'); + + + $context = 'class ' . $class->getName(); + $docComment = $class->getDocComment(); + + $parser->setTarget(Target::TARGET_CLASS); + $this->assertNotNull($parser->parse($docComment,$context)); + + + $property = $class->getProperty('foo'); + $docComment = $property->getDocComment(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + + $parser->setTarget(Target::TARGET_PROPERTY); + $this->assertNotNull($parser->parse($docComment,$context)); + + + + $method = $class->getMethod('someFunction'); + $docComment = $property->getDocComment(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + + $parser->setTarget(Target::TARGET_METHOD); + $this->assertNotNull($parser->parse($docComment,$context)); + + + try { + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass'); + $context = 'class ' . $class->getName(); + $docComment = $class->getDocComment(); + + $parser->setTarget(Target::TARGET_CLASS); + $parser->parse($class->getDocComment(),$context); + + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertNotNull($exc->getMessage()); + } + + + try { + + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod'); + $method = $class->getMethod('functionName'); + $docComment = $method->getDocComment(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + + $parser->setTarget(Target::TARGET_METHOD); + $parser->parse($docComment,$context); + + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertNotNull($exc->getMessage()); + } + + + try { + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty'); + $property = $class->getProperty('foo'); + $docComment = $property->getDocComment(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + + $parser->setTarget(Target::TARGET_PROPERTY); + $parser->parse($docComment,$context); + + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertNotNull($exc->getMessage()); + } + + } + + public function getAnnotationVarTypeProviderValid() + { + //({attribute name}, {attribute value}) + return array( + // mixed type + array('mixed', '"String Value"'), + array('mixed', 'true'), + array('mixed', 'false'), + array('mixed', '1'), + array('mixed', '1.2'), + array('mixed', '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll'), + + // boolean type + array('boolean', 'true'), + array('boolean', 'false'), + + // alias for internal type boolean + array('bool', 'true'), + array('bool', 'false'), + + // integer type + array('integer', '0'), + array('integer', '1'), + array('integer', '123456789'), + array('integer', '9223372036854775807'), + + // alias for internal type double + array('float', '0.1'), + array('float', '1.2'), + array('float', '123.456'), + + // string type + array('string', '"String Value"'), + array('string', '"true"'), + array('string', '"123"'), + + // array type + array('array', '{@AnnotationExtendsAnnotationTargetAll}'), + array('array', '{@AnnotationExtendsAnnotationTargetAll,@AnnotationExtendsAnnotationTargetAll}'), + + array('arrayOfIntegers', '1'), + array('arrayOfIntegers', '{1}'), + array('arrayOfIntegers', '{1,2,3,4}'), + array('arrayOfAnnotations', '@AnnotationExtendsAnnotationTargetAll'), + array('arrayOfAnnotations', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll}'), + array('arrayOfAnnotations', '{@AnnotationExtendsAnnotationTargetAll, @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll}'), + + // annotation instance + array('annotation', '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll'), + array('annotation', '@AnnotationExtendsAnnotationTargetAll'), + ); + } + + public function getAnnotationVarTypeProviderInvalid() + { + //({attribute name}, {type declared type}, {attribute value} , {given type or class}) + return array( + // boolean type + array('boolean','boolean','1','integer'), + array('boolean','boolean','1.2','double'), + array('boolean','boolean','"str"','string'), + array('boolean','boolean','{1,2,3}','array'), + array('boolean','boolean','@Name', 'an instance of Doctrine\Tests\Common\Annotations\Name'), + + // alias for internal type boolean + array('bool','bool', '1','integer'), + array('bool','bool', '1.2','double'), + array('bool','bool', '"str"','string'), + array('bool','bool', '{"str"}','array'), + + // integer type + array('integer','integer', 'true','boolean'), + array('integer','integer', 'false','boolean'), + array('integer','integer', '1.2','double'), + array('integer','integer', '"str"','string'), + array('integer','integer', '{"str"}','array'), + array('integer','integer', '{1,2,3,4}','array'), + + // alias for internal type double + array('float','float', 'true','boolean'), + array('float','float', 'false','boolean'), + array('float','float', '123','integer'), + array('float','float', '"str"','string'), + array('float','float', '{"str"}','array'), + array('float','float', '{12.34}','array'), + array('float','float', '{1,2,3}','array'), + + // string type + array('string','string', 'true','boolean'), + array('string','string', 'false','boolean'), + array('string','string', '12','integer'), + array('string','string', '1.2','double'), + array('string','string', '{"str"}','array'), + array('string','string', '{1,2,3,4}','array'), + + // annotation instance + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'true','boolean'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'false','boolean'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '12','integer'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '1.2','double'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{"str"}','array'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{1,2,3,4}','array'), + array('annotation','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '@Name','an instance of Doctrine\Tests\Common\Annotations\Name'), + ); + } + + public function getAnnotationVarTypeArrayProviderInvalid() + { + //({attribute name}, {type declared type}, {attribute value} , {given type or class}) + return array( + array('arrayOfIntegers','integer', 'true','boolean'), + array('arrayOfIntegers','integer', 'false','boolean'), + array('arrayOfIntegers','integer', '{true,true}','boolean'), + array('arrayOfIntegers','integer', '{1,true}','boolean'), + array('arrayOfIntegers','integer', '{1,2,1.2}','double'), + array('arrayOfIntegers','integer', '{1,2,"str"}','string'), + + + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'true','boolean'), + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', 'false','boolean'), + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,true}','boolean'), + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,true}','boolean'), + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,1.2}','double'), + array('arrayOfAnnotations','Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', '{@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll,@AnnotationExtendsAnnotationTargetAll,"str"}','string'), + ); + } + + /** + * @dataProvider getAnnotationVarTypeProviderValid + */ + public function testAnnotationWithVarType($attribute, $value) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::$invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + $result = $parser->parse($docblock, $context); + + $this->assertTrue(sizeof($result) === 1); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType', $result[0]); + $this->assertNotNull($result[0]->$attribute); + } + + /** + * @dataProvider getAnnotationVarTypeProviderInvalid + */ + public function testAnnotationWithVarTypeError($attribute,$type,$value,$given) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + try { + $parser->parse($docblock, $context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType declared on property SomeClassName::invalidProperty. expects a(n) $type, but got $given.", $exc->getMessage()); + } + } + + + /** + * @dataProvider getAnnotationVarTypeArrayProviderInvalid + */ + public function testAnnotationWithVarTypeArrayError($attribute,$type,$value,$given) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + try { + $parser->parse($docblock, $context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType declared on property SomeClassName::invalidProperty. expects either a(n) $type, or an array of {$type}s, but got $given.", $exc->getMessage()); + } + } + + /** + * @dataProvider getAnnotationVarTypeProviderValid + */ + public function testAnnotationWithAttributes($attribute, $value) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::$invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + $result = $parser->parse($docblock, $context); + + $this->assertTrue(sizeof($result) === 1); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes', $result[0]); + $getter = "get".ucfirst($attribute); + $this->assertNotNull($result[0]->$getter()); + } + + /** + * @dataProvider getAnnotationVarTypeProviderInvalid + */ + public function testAnnotationWithAttributesError($attribute,$type,$value,$given) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + try { + $parser->parse($docblock, $context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes declared on property SomeClassName::invalidProperty. expects a(n) $type, but got $given.", $exc->getMessage()); + } + } + + + /** + * @dataProvider getAnnotationVarTypeArrayProviderInvalid + */ + public function testAnnotationWithAttributesWithVarTypeArrayError($attribute,$type,$value,$given) + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = sprintf('@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes(%s = %s)',$attribute, $value); + $parser->setTarget(Target::TARGET_PROPERTY); + + try { + $parser->parse($docblock, $context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains("[Type Error] Attribute \"$attribute\" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithAttributes declared on property SomeClassName::invalidProperty. expects either a(n) $type, or an array of {$type}s, but got $given.", $exc->getMessage()); + } + } + + public function testAnnotationWithRequiredAttributes() + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $parser->setTarget(Target::TARGET_PROPERTY); + + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes("Some Value", annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)'; + $result = $parser->parse($docblock); + + $this->assertTrue(sizeof($result) === 1); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes', $result[0]); + $this->assertEquals("Some Value",$result[0]->getValue()); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation', $result[0]->getAnnot()); + + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes("Some Value")'; + try { + $result = $parser->parse($docblock,$context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains('Attribute "annot" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes declared on property SomeClassName::invalidProperty. expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation. This value should not be null.', $exc->getMessage()); + } + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes(annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)'; + try { + $result = $parser->parse($docblock,$context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains('Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributes declared on property SomeClassName::invalidProperty. expects a(n) string. This value should not be null.', $exc->getMessage()); + } + + } + + public function testAnnotationWithRequiredAttributesWithoutContructor() + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $parser->setTarget(Target::TARGET_PROPERTY); + + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor("Some Value", annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)'; + $result = $parser->parse($docblock); + + $this->assertTrue(sizeof($result) === 1); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor', $result[0]); + $this->assertEquals("Some Value", $result[0]->value); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation', $result[0]->annot); + + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor("Some Value")'; + try { + $result = $parser->parse($docblock,$context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains('Attribute "annot" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor declared on property SomeClassName::invalidProperty. expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation. This value should not be null.', $exc->getMessage()); + } + + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor(annot = @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation)'; + try { + $result = $parser->parse($docblock,$context); + $this->fail(); + } catch (\Doctrine\Common\Annotations\AnnotationException $exc) { + $this->assertContains('Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithRequiredAttributesWithoutContructor declared on property SomeClassName::invalidProperty. expects a(n) string. This value should not be null.', $exc->getMessage()); + } + + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property SomeClassName::invalidProperty. accept only [ONE, TWO, THREE], but got FOUR. + */ + public function testAnnotationEnumeratorException() + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum("FOUR")'; + + $parser->setIgnoreNotImportedAnnotations(false); + $parser->setTarget(Target::TARGET_PROPERTY); + $parser->parse($docblock, $context); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral declared on property SomeClassName::invalidProperty. accept only [AnnotationEnumLiteral::ONE, AnnotationEnumLiteral::TWO, AnnotationEnumLiteral::THREE], but got 4. + */ + public function testAnnotationEnumeratorLiteralException() + { + $parser = $this->createTestParser(); + $context = 'property SomeClassName::invalidProperty.'; + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral(4)'; + + $parser->setIgnoreNotImportedAnnotations(false); + $parser->setTarget(Target::TARGET_PROPERTY); + $parser->parse($docblock, $context); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage @Enum supports only scalar values "array" given. + */ + public function testAnnotationEnumInvalidTypeDeclarationException() + { + $parser = $this->createTestParser(); + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumInvalid("foo")'; + + $parser->setIgnoreNotImportedAnnotations(false); + $parser->parse($docblock); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Undefined enumerator value "3" for literal "AnnotationEnumLiteral::THREE". + */ + public function testAnnotationEnumInvalidLiteralDeclarationException() + { + $parser = $this->createTestParser(); + $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteralInvalid("foo")'; + + $parser->setIgnoreNotImportedAnnotations(false); + $parser->parse($docblock); + } + + public function getConstantsProvider() + { + $provider[] = array( + '@AnnotationWithConstants(PHP_EOL)', + PHP_EOL + ); + $provider[] = array( + '@AnnotationWithConstants(AnnotationWithConstants::INTEGER)', + AnnotationWithConstants::INTEGER + ); + $provider[] = array( + '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants(AnnotationWithConstants::STRING)', + AnnotationWithConstants::STRING + ); + $provider[] = array( + '@AnnotationWithConstants(Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants::FLOAT)', + AnnotationWithConstants::FLOAT + ); + $provider[] = array( + '@AnnotationWithConstants(ClassWithConstants::SOME_VALUE)', + ClassWithConstants::SOME_VALUE + ); + $provider[] = array( + '@AnnotationWithConstants(Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants::SOME_VALUE)', + ClassWithConstants::SOME_VALUE + ); + $provider[] = array( + '@AnnotationWithConstants(IntefaceWithConstants::SOME_VALUE)', + IntefaceWithConstants::SOME_VALUE + ); + $provider[] = array( + '@AnnotationWithConstants(\Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)', + IntefaceWithConstants::SOME_VALUE + ); + $provider[] = array( + '@AnnotationWithConstants({AnnotationWithConstants::STRING, AnnotationWithConstants::INTEGER, AnnotationWithConstants::FLOAT})', + array(AnnotationWithConstants::STRING, AnnotationWithConstants::INTEGER, AnnotationWithConstants::FLOAT) + ); + $provider[] = array( + '@AnnotationWithConstants({ + AnnotationWithConstants::STRING = AnnotationWithConstants::INTEGER + })', + array(AnnotationWithConstants::STRING => AnnotationWithConstants::INTEGER) + ); + $provider[] = array( + '@AnnotationWithConstants({ + Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_KEY = AnnotationWithConstants::INTEGER + })', + array(IntefaceWithConstants::SOME_KEY => AnnotationWithConstants::INTEGER) + ); + $provider[] = array( + '@AnnotationWithConstants({ + \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_KEY = AnnotationWithConstants::INTEGER + })', + array(IntefaceWithConstants::SOME_KEY => AnnotationWithConstants::INTEGER) + ); + $provider[] = array( + '@AnnotationWithConstants({ + AnnotationWithConstants::STRING = AnnotationWithConstants::INTEGER, + ClassWithConstants::SOME_KEY = ClassWithConstants::SOME_VALUE, + Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants::SOME_KEY = IntefaceWithConstants::SOME_VALUE + })', + array( + AnnotationWithConstants::STRING => AnnotationWithConstants::INTEGER, + ClassWithConstants::SOME_KEY => ClassWithConstants::SOME_VALUE, + ClassWithConstants::SOME_KEY => IntefaceWithConstants::SOME_VALUE + ) + ); + return $provider; + } + + /** + * @dataProvider getConstantsProvider + */ + public function testSupportClassConstants($docblock, $expected) + { + $parser = $this->createTestParser(); + $parser->setImports(array( + 'classwithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\ClassWithConstants', + 'intefacewithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants', + 'annotationwithconstants' => 'Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants' + )); + + $result = $parser->parse($docblock); + $this->assertInstanceOf('\Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithConstants', $annotation = $result[0]); + $this->assertEquals($expected, $annotation->value); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage The annotation @SomeAnnotationClassNameWithoutConstructorAndProperties declared on does not accept any values, but got {"value":"Foo"}. + */ + public function testWithoutConstructorWhenIsNotDefaultValue() + { + $parser = $this->createTestParser(); + $docblock = <<setTarget(Target::TARGET_CLASS); + $parser->parse($docblock); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage The annotation @SomeAnnotationClassNameWithoutConstructorAndProperties declared on does not accept any values, but got {"value":"Foo"}. + */ + public function testWithoutConstructorWhenHasNoProperties() + { + $parser = $this->createTestParser(); + $docblock = <<setTarget(Target::TARGET_CLASS); + $parser->parse($docblock); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError. + */ + public function testAnnotationTargetSyntaxError() + { + $parser = $this->createTestParser(); + $context = 'class ' . 'SomeClassName'; + $docblock = <<setTarget(Target::TARGET_CLASS); + $parser->parse($docblock,$context); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid Target "Foo". Available targets: [ALL, CLASS, METHOD, PROPERTY, ANNOTATION] + */ + public function testAnnotationWithInvalidTargetDeclarationError() + { + $parser = $this->createTestParser(); + $context = 'class ' . 'SomeClassName'; + $docblock = <<setTarget(Target::TARGET_CLASS); + $parser->parse($docblock,$context); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage @Target expects either a string value, or an array of strings, "NULL" given. + */ + public function testAnnotationWithTargetEmptyError() + { + $parser = $this->createTestParser(); + $context = 'class ' . 'SomeClassName'; + $docblock = <<setTarget(Target::TARGET_CLASS); + $parser->parse($docblock,$context); + } + + /** + * @group DDC-575 + */ + public function testRegressionDDC575() + { + $parser = $this->createTestParser(); + + $docblock = <<parse($docblock); + + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Name", $result[0]); + + $docblock = <<parse($docblock); + + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Name", $result[0]); + } + + /** + * @group DDC-77 + */ + public function testAnnotationWithoutClassIsIgnoredWithoutWarning() + { + $parser = new DocParser(); + $parser->setIgnoreNotImportedAnnotations(true); + $result = $parser->parse("@param"); + + $this->assertEquals(0, count($result)); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected PlainValue, got ''' at position 10. + */ + public function testAnnotationDontAcceptSingleQuotes() + { + $parser = $this->createTestParser(); + $parser->parse("@Name(foo='bar')"); + } + + /** + * @group DCOM-41 + */ + public function testAnnotationDoesntThrowExceptionWhenAtSignIsNotFollowedByIdentifier() + { + $parser = new DocParser(); + $result = $parser->parse("'@'"); + + $this->assertEquals(0, count($result)); + } + + /** + * @group DCOM-41 + * @expectedException Doctrine\Common\Annotations\AnnotationException + */ + public function testAnnotationThrowsExceptionWhenAtSignIsNotFollowedByIdentifierInNestedAnnotation() + { + $parser = new DocParser(); + $result = $parser->parse("@Doctrine\Tests\Common\Annotations\Name(@')"); + } + + /** + * @group DCOM-56 + */ + public function testAutoloadAnnotation() + { + $this->assertFalse(class_exists('Doctrine\Tests\Common\Annotations\Fixture\Annotation\Autoload', false), 'Pre-condition: Doctrine\Tests\Common\Annotations\Fixture\Annotation\Autoload not allowed to be loaded.'); + + $parser = new DocParser(); + + AnnotationRegistry::registerAutoloadNamespace('Doctrine\Tests\Common\Annotations\Fixtures\Annotation', __DIR__ . '/../../../../'); + + $parser->setImports(array( + 'autoload' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Autoload', + )); + $annotations = $parser->parse('@Autoload'); + + $this->assertEquals(1, count($annotations)); + $this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Autoload', $annotations[0]); + } + + public function createTestParser() + { + $parser = new DocParser(); + $parser->setIgnoreNotImportedAnnotations(true); + $parser->setImports(array( + 'name' => 'Doctrine\Tests\Common\Annotations\Name', + '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations', + )); + + return $parser; + } + + /** + * @group DDC-78 + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage Expected PlainValue, got ''' at position 10 in class \Doctrine\Tests\Common\Annotations\Name + */ + public function testSyntaxErrorWithContextDescription() + { + $parser = $this->createTestParser(); + $parser->parse("@Name(foo='bar')", "class \Doctrine\Tests\Common\Annotations\Name"); + } + + /** + * @group DDC-183 + */ + public function testSyntaxErrorWithUnknownCharacters() + { + $docblock = <<setInput(trim($docblock, '/ *')); + //var_dump($lexer); + + try { + $parser = $this->createTestParser(); + $result = $parser->parse($docblock); + } catch (Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @group DCOM-14 + */ + public function testIgnorePHPDocThrowTag() + { + $docblock = <<createTestParser(); + $result = $parser->parse($docblock); + } catch (Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @group DCOM-38 + */ + public function testCastInt() + { + $parser = $this->createTestParser(); + + $result = $parser->parse("@Name(foo=1234)"); + $annot = $result[0]; + $this->assertInternalType('int', $annot->foo); + } + + /** + * @group DCOM-38 + */ + public function testCastNegativeInt() + { + $parser = $this->createTestParser(); + + $result = $parser->parse("@Name(foo=-1234)"); + $annot = $result[0]; + $this->assertInternalType('int', $annot->foo); + } + + /** + * @group DCOM-38 + */ + public function testCastFloat() + { + $parser = $this->createTestParser(); + + $result = $parser->parse("@Name(foo=1234.345)"); + $annot = $result[0]; + $this->assertInternalType('float', $annot->foo); + } + + /** + * @group DCOM-38 + */ + public function testCastNegativeFloat() + { + $parser = $this->createTestParser(); + + $result = $parser->parse("@Name(foo=-1234.345)"); + $annot = $result[0]; + $this->assertInternalType('float', $annot->foo); + + $result = $parser->parse("@Marker(-1234.345)"); + $annot = $result[0]; + $this->assertInternalType('float', $annot->value); + } + + public function testReservedKeywordsInAnnotations() + { + $parser = $this->createTestParser(); + + $result = $parser->parse('@Doctrine\Tests\Common\Annotations\True'); + $this->assertTrue($result[0] instanceof True); + $result = $parser->parse('@Doctrine\Tests\Common\Annotations\False'); + $this->assertTrue($result[0] instanceof False); + $result = $parser->parse('@Doctrine\Tests\Common\Annotations\Null'); + $this->assertTrue($result[0] instanceof Null); + + $result = $parser->parse('@True'); + $this->assertTrue($result[0] instanceof True); + $result = $parser->parse('@False'); + $this->assertTrue($result[0] instanceof False); + $result = $parser->parse('@Null'); + $this->assertTrue($result[0] instanceof Null); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Creation Error] The annotation @SomeAnnotationClassNameWithoutConstructor declared on some class does not have a property named "invalidaProperty". Available properties: data, name + */ + public function testSetValuesExeption() + { + $docblock = <<createTestParser()->parse($docblock, 'some class'); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Syntax Error] Expected Doctrine\Common\Annotations\DocLexer::T_IDENTIFIER or Doctrine\Common\Annotations\DocLexer::T_TRUE or Doctrine\Common\Annotations\DocLexer::T_FALSE or Doctrine\Common\Annotations\DocLexer::T_NULL, got '3.42' at position 5. + */ + public function testInvalidIdentifierInAnnotation() + { + $parser = $this->createTestParser(); + $parser->parse('@Foo\3.42'); + } + + public function testTrailingCommaIsAllowed() + { + $parser = $this->createTestParser(); + + $annots = $parser->parse('@Name({ + "Foo", + "Bar", + })'); + $this->assertEquals(1, count($annots)); + $this->assertEquals(array('Foo', 'Bar'), $annots[0]->value); + } + + public function testDefaultAnnotationValueIsNotOverwritten() + { + $parser = $this->createTestParser(); + + $annots = $parser->parse('@Doctrine\Tests\Common\Annotations\Fixtures\Annotation\AnnotWithDefaultValue'); + $this->assertEquals(1, count($annots)); + $this->assertEquals('bar', $annots[0]->foo); + } + + public function testArrayWithColon() + { + $parser = $this->createTestParser(); + + $annots = $parser->parse('@Name({"foo": "bar"})'); + $this->assertEquals(1, count($annots)); + $this->assertEquals(array('foo' => 'bar'), $annots[0]->value); + } + + /** + * @expectedException Doctrine\Common\Annotations\AnnotationException + * @expectedExceptionMessage [Semantical Error] Couldn't find constant foo. + */ + public function testInvalidContantName() + { + $parser = $this->createTestParser(); + $parser->parse('@Name(foo: "bar")'); + } +} + +/** @Annotation */ +class SomeAnnotationClassNameWithoutConstructor +{ + public $data; + public $name; +} + +/** @Annotation */ +class SomeAnnotationWithConstructorWithoutParams +{ + function __construct() + { + $this->data = "Some data"; + } + public $data; + public $name; +} + +/** @Annotation */ +class SomeAnnotationClassNameWithoutConstructorAndProperties{} + +/** + * @Annotation + * @Target("Foo") + */ +class AnnotationWithInvalidTargetDeclaration{} + +/** + * @Annotation + * @Target + */ +class AnnotationWithTargetEmpty{} + +/** @Annotation */ +class AnnotationExtendsAnnotationTargetAll extends \Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll +{ +} + +/** @Annotation */ +class Name extends \Doctrine\Common\Annotations\Annotation { + public $foo; +} + +/** @Annotation */ +class Marker { + public $value; +} + +/** @Annotation */ +class True {} + +/** @Annotation */ +class False {} + +/** @Annotation */ +class Null {} + +namespace Doctrine\Tests\Common\Annotations\FooBar; + +/** @Annotation */ +class Name extends \Doctrine\Common\Annotations\Annotation { +} diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.php new file mode 100644 index 00000000..17223f68 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/DummyClass.php @@ -0,0 +1,48 @@ +cacheDir = sys_get_temp_dir() . "/annotations_". uniqid(); + @mkdir($this->cacheDir); + return new FileCacheReader(new AnnotationReader(), $this->cacheDir); + } + + public function tearDown() + { + foreach (glob($this->cacheDir.'/*.php') AS $file) { + unlink($file); + } + rmdir($this->cacheDir); + } + + /** + * @group DCOM-81 + */ + public function testAttemptToCreateAnnotationCacheDir() + { + $this->cacheDir = sys_get_temp_dir() . "/not_existed_dir_". uniqid(); + + $this->assertFalse(is_dir($this->cacheDir)); + + $cache = new FileCacheReader(new AnnotationReader(), $this->cacheDir); + + $this->assertTrue(is_dir($this->cacheDir)); + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php new file mode 100644 index 00000000..44108e19 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/AnnotWithDefaultValue.php @@ -0,0 +1,10 @@ +roles = $values['value']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php new file mode 100644 index 00000000..b507e602 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Template.php @@ -0,0 +1,14 @@ +name = isset($values['value']) ? $values['value'] : null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php new file mode 100644 index 00000000..09ef0317 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Annotation/Version.php @@ -0,0 +1,11 @@ +"), + @Attribute("annotation", type = "Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll"), + @Attribute("arrayOfAnnotations", type = "array"), + }) + */ +final class AnnotationWithAttributes +{ + + public final function __construct(array $data) + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + private $mixed; + private $boolean; + private $bool; + private $float; + private $string; + private $integer; + private $array; + private $annotation; + private $arrayOfIntegers; + private $arrayOfAnnotations; + + /** + * @return mixed + */ + public function getMixed() + { + return $this->mixed; + } + + /** + * @return boolean + */ + public function getBoolean() + { + return $this->boolean; + } + + /** + * @return bool + */ + public function getBool() + { + return $this->bool; + } + + /** + * @return float + */ + public function getFloat() + { + return $this->float; + } + + /** + * @return string + */ + public function getString() + { + return $this->string; + } + + public function getInteger() + { + return $this->integer; + } + + /** + * @return array + */ + public function getArray() + { + return $this->array; + } + + /** + * @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll + */ + public function getAnnotation() + { + return $this->annotation; + } + + /** + * @return array + */ + public function getArrayOfIntegers() + { + return $this->arrayOfIntegers; + } + + /** + * @return array + */ + public function getArrayOfAnnotations() + { + return $this->arrayOfAnnotations; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php new file mode 100644 index 00000000..9c94558b --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithConstants.php @@ -0,0 +1,20 @@ + $value) { + $this->$key = $value; + } + } + + /** + * @var string + */ + private $value; + + /** + * + * @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation + */ + private $annot; + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation + */ + public function getAnnot() + { + return $this->annot; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.php new file mode 100644 index 00000000..bf458ee7 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationWithRequiredAttributesWithoutContructor.php @@ -0,0 +1,24 @@ + + */ + public $arrayOfIntegers; + + /** + * @var array + */ + public $arrayOfAnnotations; + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.php new file mode 100644 index 00000000..534ad142 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/Api.php @@ -0,0 +1,10 @@ +events->filter(function ($item) use ($year, $month, $day) { + $leftDate = new \DateTime($year.'-'.$month.'-'.$day.' 00:00'); + $rigthDate = new \DateTime($year.'-'.$month.'-'.$day.' +1 day 00:00'); + return ( ( $leftDate <= $item->getDateStart() ) && ( $item->getDateStart() < $rigthDate ) ); + + } + ); + return $extractEvents; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php new file mode 100644 index 00000000..055e245c --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithConstants.php @@ -0,0 +1,10 @@ + + */ +class Controller +{ + /** + * @Route("/", name="_demo") + * @Template() + */ + public function indexAction() + { + return array(); + } + + /** + * @Route("/hello/{name}", name="_demo_hello") + * @Template() + */ + public function helloAction($name) + { + return array('name' => $name); + } + + /** + * @Route("/contact", name="_demo_contact") + * @Template() + */ + public function contactAction() + { + $form = ContactForm::create($this->get('form.context'), 'contact'); + + $form->bind($this->container->get('request'), $form); + if ($form->isValid()) { + $form->send($this->get('mailer')); + + $this->get('session')->setFlash('notice', 'Message sent!'); + + return new RedirectResponse($this->generateUrl('_demo')); + } + + return array('form' => $form); + } + + /** + * Creates the ACL for the passed object identity + * + * @param ObjectIdentityInterface $oid + * @return void + */ + private function createObjectIdentity(ObjectIdentityInterface $oid) + { + $classId = $this->createOrRetrieveClassId($oid->getType()); + + $this->connection->executeQuery($this->getInsertObjectIdentitySql($oid->getIdentifier(), $classId, true)); + } + + /** + * Returns the primary key for the passed class type. + * + * If the type does not yet exist in the database, it will be created. + * + * @param string $classType + * @return integer + */ + private function createOrRetrieveClassId($classType) + { + if (false !== $id = $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn()) { + return $id; + } + + $this->connection->executeQuery($this->getInsertClassSql($classType)); + + return $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn(); + } + + /** + * Returns the primary key for the passed security identity. + * + * If the security identity does not yet exist in the database, it will be + * created. + * + * @param SecurityIdentityInterface $sid + * @return integer + */ + private function createOrRetrieveSecurityIdentityId(SecurityIdentityInterface $sid) + { + if (false !== $id = $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn()) { + return $id; + } + + $this->connection->executeQuery($this->getInsertSecurityIdentitySql($sid)); + + return $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn(); + } + + /** + * Deletes all ACEs for the given object identity primary key. + * + * @param integer $oidPK + * @return void + */ + private function deleteAccessControlEntries($oidPK) + { + $this->connection->executeQuery($this->getDeleteAccessControlEntriesSql($oidPK)); + } + + /** + * Deletes the object identity from the database. + * + * @param integer $pk + * @return void + */ + private function deleteObjectIdentity($pk) + { + $this->connection->executeQuery($this->getDeleteObjectIdentitySql($pk)); + } + + /** + * Deletes all entries from the relations table from the database. + * + * @param integer $pk + * @return void + */ + private function deleteObjectIdentityRelations($pk) + { + $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk)); + } + + /** + * This regenerates the ancestor table which is used for fast read access. + * + * @param AclInterface $acl + * @return void + */ + private function regenerateAncestorRelations(AclInterface $acl) + { + $pk = $acl->getId(); + $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk)); + $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $pk)); + + $parentAcl = $acl->getParentAcl(); + while (null !== $parentAcl) { + $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $parentAcl->getId())); + + $parentAcl = $parentAcl->getParentAcl(); + } + } + + /** + * This processes changes on an ACE related property (classFieldAces, or objectFieldAces). + * + * @param string $name + * @param array $changes + * @return void + */ + private function updateFieldAceProperty($name, array $changes) + { + $sids = new \SplObjectStorage(); + $classIds = new \SplObjectStorage(); + $currentIds = array(); + foreach ($changes[1] as $field => $new) { + for ($i=0,$c=count($new); $i<$c; $i++) { + $ace = $new[$i]; + + if (null === $ace->getId()) { + if ($sids->contains($ace->getSecurityIdentity())) { + $sid = $sids->offsetGet($ace->getSecurityIdentity()); + } else { + $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity()); + } + + $oid = $ace->getAcl()->getObjectIdentity(); + if ($classIds->contains($oid)) { + $classId = $classIds->offsetGet($oid); + } else { + $classId = $this->createOrRetrieveClassId($oid->getType()); + } + + $objectIdentityId = $name === 'classFieldAces' ? null : $ace->getAcl()->getId(); + + $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, $field, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure())); + $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, $field, $i))->fetchColumn(); + $this->loadedAces[$aceId] = $ace; + + $aceIdProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'id'); + $aceIdProperty->setAccessible(true); + $aceIdProperty->setValue($ace, intval($aceId)); + } else { + $currentIds[$ace->getId()] = true; + } + } + } + + foreach ($changes[0] as $old) { + for ($i=0,$c=count($old); $i<$c; $i++) { + $ace = $old[$i]; + + if (!isset($currentIds[$ace->getId()])) { + $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId())); + unset($this->loadedAces[$ace->getId()]); + } + } + } + } + + /** + * This processes changes on an ACE related property (classAces, or objectAces). + * + * @param string $name + * @param array $changes + * @return void + */ + private function updateAceProperty($name, array $changes) + { + list($old, $new) = $changes; + + $sids = new \SplObjectStorage(); + $classIds = new \SplObjectStorage(); + $currentIds = array(); + for ($i=0,$c=count($new); $i<$c; $i++) { + $ace = $new[$i]; + + if (null === $ace->getId()) { + if ($sids->contains($ace->getSecurityIdentity())) { + $sid = $sids->offsetGet($ace->getSecurityIdentity()); + } else { + $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity()); + } + + $oid = $ace->getAcl()->getObjectIdentity(); + if ($classIds->contains($oid)) { + $classId = $classIds->offsetGet($oid); + } else { + $classId = $this->createOrRetrieveClassId($oid->getType()); + } + + $objectIdentityId = $name === 'classAces' ? null : $ace->getAcl()->getId(); + + $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, null, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure())); + $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, null, $i))->fetchColumn(); + $this->loadedAces[$aceId] = $ace; + + $aceIdProperty = new \ReflectionProperty($ace, 'id'); + $aceIdProperty->setAccessible(true); + $aceIdProperty->setValue($ace, intval($aceId)); + } else { + $currentIds[$ace->getId()] = true; + } + } + + for ($i=0,$c=count($old); $i<$c; $i++) { + $ace = $old[$i]; + + if (!isset($currentIds[$ace->getId()])) { + $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId())); + unset($this->loadedAces[$ace->getId()]); + } + } + } + + /** + * Persists the changes which were made to ACEs to the database. + * + * @param \SplObjectStorage $aces + * @return void + */ + private function updateAces(\SplObjectStorage $aces) + { + foreach ($aces as $ace) { + $propertyChanges = $aces->offsetGet($ace); + $sets = array(); + + if (isset($propertyChanges['mask'])) { + $sets[] = sprintf('mask = %d', $propertyChanges['mask'][1]); + } + if (isset($propertyChanges['strategy'])) { + $sets[] = sprintf('granting_strategy = %s', $this->connection->quote($propertyChanges['strategy'])); + } + if (isset($propertyChanges['aceOrder'])) { + $sets[] = sprintf('ace_order = %d', $propertyChanges['aceOrder'][1]); + } + if (isset($propertyChanges['auditSuccess'])) { + $sets[] = sprintf('audit_success = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditSuccess'][1])); + } + if (isset($propertyChanges['auditFailure'])) { + $sets[] = sprintf('audit_failure = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditFailure'][1])); + } + + $this->connection->executeQuery($this->getUpdateAccessControlEntrySql($ace->getId(), $sets)); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php new file mode 100644 index 00000000..bda2cc21 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/DifferentNamespacesPerFileWithClassAsFirst.php @@ -0,0 +1,15 @@ +test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test2() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test3() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test4() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test5() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test6() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test7() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test8() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test9() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test10() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test11() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test12() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test13() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test14() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test15() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test16() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test17() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test18() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test19() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test20() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test21() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test22() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test23() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test24() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test25() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test26() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test27() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test28() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test29() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test30() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test31() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test32() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test33() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test34() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test35() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test36() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test37() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test38() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test39() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.php new file mode 100644 index 00000000..1dae104a --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/NoAnnotation.php @@ -0,0 +1,5 @@ +test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test2() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test3() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test4() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test5() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test6() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test7() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test8() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test9() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test10() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test11() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test12() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test13() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test14() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test15() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test16() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test17() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test18() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test19() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test20() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test21() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test22() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test23() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test24() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test25() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test26() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test27() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test28() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test29() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test30() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test31() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test32() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test33() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test34() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test35() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test36() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test37() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + + } + + public function test38() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } + + public function test39() + { + echo $this->test1; + echo $this->test2; + echo $this->test3; + $array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + foreach ($array as $key => $value) { + echo $key . ' => ' . $value; + } + + $val = (string)self::TEST1; + $val .= (string)self::TEST2; + $val .= (string)self::TEST3; + $val .= (string)self::TEST4; + $val .= (string)self::TEST5; + $val .= (string)self::TEST6; + $val .= (string)self::TEST7; + $val .= (string)self::TEST8; + $val .= (string)self::TEST9; + + strtolower($val); + + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php new file mode 100644 index 00000000..58c5e6af --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Fixtures/TestInterface.php @@ -0,0 +1,13 @@ +getMethod(); + + $time = microtime(true); + for ($i=0,$c=500; $i<$c; $i++) { + $reader->getMethodAnnotations($method); + } + $time = microtime(true) - $time; + + $this->printResults('cached reader (in-memory)', $time, $c); + } + + /** + * @group performance + */ + public function testCachedReadPerformanceWithFileCache() + { + $method = $this->getMethod(); + + // prime cache + $reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir()); + $reader->getMethodAnnotations($method); + + $time = microtime(true); + for ($i=0,$c=500; $i<$c; $i++) { + $reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir()); + $reader->getMethodAnnotations($method); + clearstatcache(); + } + $time = microtime(true) - $time; + + $this->printResults('cached reader (file)', $time, $c); + } + + /** + * @group performance + */ + public function testReadPerformance() + { + $method = $this->getMethod(); + + $time = microtime(true); + for ($i=0,$c=150; $i<$c; $i++) { + $reader = new AnnotationReader(); + $reader->getMethodAnnotations($method); + } + $time = microtime(true) - $time; + + $this->printResults('reader', $time, $c); + } + + /** + * @group performance + */ + public function testDocParsePerformance() + { + $imports = array( + 'ignorephpdoc' => 'Annotations\Annotation\IgnorePhpDoc', + 'ignoreannotation' => 'Annotations\Annotation\IgnoreAnnotation', + 'route' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route', + 'template' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template', + '__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations\Fixtures', + ); + $ignored = array( + 'access', 'author', 'copyright', 'deprecated', 'example', 'ignore', + 'internal', 'link', 'see', 'since', 'tutorial', 'version', 'package', + 'subpackage', 'name', 'global', 'param', 'return', 'staticvar', + 'static', 'var', 'throws', 'inheritdoc', + ); + + $method = $this->getMethod(); + $methodComment = $method->getDocComment(); + $classComment = $method->getDeclaringClass()->getDocComment(); + + $time = microtime(true); + for ($i=0,$c=200; $i<$c; $i++) { + $parser = new DocParser(); + $parser->setImports($imports); + $parser->setIgnoredAnnotationNames($ignored); + $parser->setIgnoreNotImportedAnnotations(true); + + $parser->parse($methodComment); + $parser->parse($classComment); + } + $time = microtime(true) - $time; + + $this->printResults('doc-parser', $time, $c); + } + + /** + * @group performance + */ + public function testDocLexerPerformance() + { + $method = $this->getMethod(); + $methodComment = $method->getDocComment(); + $classComment = $method->getDeclaringClass()->getDocComment(); + + $time = microtime(true); + for ($i=0,$c=500; $i<$c; $i++) { + $lexer = new DocLexer(); + $lexer->setInput($methodComment); + $lexer->setInput($classComment); + } + $time = microtime(true) - $time; + + $this->printResults('doc-lexer', $time, $c); + } + + /** + * @group performance + */ + public function testPhpParserPerformanceWithShortCut() + { + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\NamespacedSingleClassLOC1000'); + + $time = microtime(true); + for ($i=0,$c=500; $i<$c; $i++) { + $parser = new PhpParser(); + $parser->parseClass($class); + } + $time = microtime(true) - $time; + + $this->printResults('doc-parser-with-short-cut', $time, $c); + } + + /** + * @group performance + */ + public function testPhpParserPerformanceWithoutShortCut() + { + $class = new \ReflectionClass('SingleClassLOC1000'); + + $time = microtime(true); + for ($i=0,$c=500; $i<$c; $i++) { + $parser = new PhpParser(); + $parser->parseClass($class); + } + $time = microtime(true) - $time; + + $this->printResults('doc-parser-without-short-cut', $time, $c); + } + + private function getMethod() + { + return new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\Controller', 'helloAction'); + } + + private function printResults($test, $time, $iterations) + { + if (0 == $iterations) { + throw new \InvalidArgumentException('$iterations cannot be zero.'); + } + + $title = $test." results:\n"; + $iterationsText = sprintf("Iterations: %d\n", $iterations); + $totalTime = sprintf("Total Time: %.3f s\n", $time); + $iterationTime = sprintf("Time per iteration: %.3f ms\n", $time/$iterations * 1000); + + $max = max(strlen($title), strlen($iterationTime)) - 1; + + echo "\n".str_repeat('-', $max)."\n"; + echo $title; + echo str_repeat('=', $max)."\n"; + echo $iterationsText; + echo $totalTime; + echo $iterationTime; + echo str_repeat('-', $max)."\n"; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php new file mode 100644 index 00000000..dc01f8b0 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php @@ -0,0 +1,207 @@ +assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + ), $parser->parseClass($class)); + } + + public function testParseClassWithMultipleImportsInUseStatement() + { + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\MultipleImportsInUseStatement'); + $parser = new PhpParser(); + + $this->assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + ), $parser->parseClass($class)); + } + + public function testParseClassWhenNotUserDefined() + { + $parser = new PhpParser(); + $this->assertEquals(array(), $parser->parseClass(new \ReflectionClass('\stdClass'))); + } + + public function testClassFileDoesNotExist() + { + $class = $this->getMockBuilder('\ReflectionClass') + ->disableOriginalConstructor() + ->getMock(); + $class->expects($this->once()) + ->method('getFilename') + ->will($this->returnValue('/valid/class/Fake.php(35) : eval()d code')); + + $parser = new PhpParser(); + $this->assertEquals(array(), $parser->parseClass($class)); + } + + public function testParseClassWhenClassIsNotNamespaced() + { + $parser = new PhpParser(); + $class = new ReflectionClass('\AnnotationsTestsFixturesNonNamespacedClass'); + + $this->assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testParseClassWhenClassIsInterface() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\TestInterface'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + ), $parser->parseClass($class)); + } + + public function testClassWithFullyQualifiedUseStatements() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithFullyQualifiedUseStatements'); + + $this->assertEquals(array( + 'secure' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testNamespaceAndClassCommentedOut() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceAndClassCommentedOut'); + + $this->assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testEqualNamespacesPerFileWithClassAsFirst() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsFirst'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + ), $parser->parseClass($class)); + } + + public function testEqualNamespacesPerFileWithClassAsLast() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsLast'); + + $this->assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testDifferentNamespacesPerFileWithClassAsFirst() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsFirst'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + ), $parser->parseClass($class)); + } + + public function testDifferentNamespacesPerFileWithClassAsLast() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsLast'); + + $this->assertEquals(array( + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testGlobalNamespacesPerFileWithClassAsFirst() + { + $parser = new PhpParser(); + $class = new \ReflectionClass('\GlobalNamespacesPerFileWithClassAsFirst'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + ), $parser->parseClass($class)); + } + + public function testGlobalNamespacesPerFileWithClassAsLast() + { + $parser = new PhpParser(); + $class = new ReflectionClass('\GlobalNamespacesPerFileWithClassAsLast'); + + $this->assertEquals(array( + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testNamespaceWithClosureDeclaration() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + public function testIfPointerResetsOnMultipleParsingTries() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration'); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + + $this->assertEquals(array( + 'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure', + 'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route', + 'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template', + ), $parser->parseClass($class)); + } + + /** + * @group DCOM-97 + * @group regression + */ + public function testClassWithClosure() + { + $parser = new PhpParser(); + $class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithClosure'); + + $this->assertEquals(array( + 'annotationtargetall' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAll', + 'annotationtargetannotation' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAnnotation', + ), $parser->parseClass($class)); + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php new file mode 100644 index 00000000..376539ff --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/SimpleAnnotationReaderTest.php @@ -0,0 +1,97 @@ +getReader(); + $class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660'); + + $this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version')); + $this->assertCount(1, $reader->getClassAnnotations($class)); + $this->assertCount(1, $reader->getMethodAnnotations($class->getMethod('bar'))); + $this->assertCount(1, $reader->getPropertyAnnotations($class->getProperty('foo'))); + } + + protected function getReader() + { + $reader = new SimpleAnnotationReader(); + $reader->addNamespace(__NAMESPACE__); + $reader->addNamespace(__NAMESPACE__ . '\Fixtures'); + $reader->addNamespace(__NAMESPACE__ . '\Fixtures\Annotation'); + + return $reader; + } +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.php new file mode 100644 index 00000000..a7b9e2f2 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM55Test.php @@ -0,0 +1,65 @@ +getClassAnnotations($class); + } + + public function testAnnotation() + { + $class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Consumer'); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $annots = $reader->getClassAnnotations($class); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf(__NAMESPACE__.'\\DCOM55Annotation', $annots[0]); + } + + public function testParseAnnotationDocblocks() + { + $class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Annotation'); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $annots = $reader->getClassAnnotations($class); + + $this->assertEquals(0, count($annots)); + } +} + +/** + * @Controller + */ +class Dummy +{ + +} + +/** + * @Annotation + */ +class DCOM55Annotation +{ + +} + +/** + * @DCOM55Annotation + */ +class DCOM55Consumer +{ + +} \ No newline at end of file diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php new file mode 100644 index 00000000..708bcc99 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php @@ -0,0 +1,8 @@ +getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass")); + + foreach ($result as $annot) { + $classAnnotations[get_class($annot)] = $annot; + } + + $this->assertTrue(!isset($classAnnotations['']), 'Class "xxx" is not a valid entity or mapped super class.'); + } + + public function testIssueGlobalNamespace() + { + $docblock = "@Entity"; + $parser = new \Doctrine\Common\Annotations\DocParser(); + $parser->setImports(array( + "__NAMESPACE__" =>"Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping" + )); + + $annots = $parser->parse($docblock); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]); + } + + public function testIssueNamespaces() + { + $docblock = "@Entity"; + $parser = new \Doctrine\Common\Annotations\DocParser(); + $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM"); + + $annots = $parser->parse($docblock); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Entity", $annots[0]); + } + + public function testIssueMultipleNamespaces() + { + $docblock = "@Entity"; + $parser = new \Doctrine\Common\Annotations\DocParser(); + $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping"); + $parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM"); + + $annots = $parser->parse($docblock); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]); + } + + public function testIssueWithNamespacesOrImports() + { + $docblock = "@Entity"; + $parser = new \Doctrine\Common\Annotations\DocParser(); + $annots = $parser->parse($docblock); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf("Entity", $annots[0]); + $this->assertEquals(1, count($annots)); + } + + + public function testIssueSimpleAnnotationReader() + { + $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping'); + $annots = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass")); + + $this->assertEquals(1, count($annots)); + $this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]); + } + +} + +/** + * @Entity + */ +class MappedClass +{ + +} + + +namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping; +/** +* @Annotation +*/ +class Entity +{ + +} + +namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM; +/** +* @Annotation +*/ +class Entity +{ + +} diff --git a/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.php b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.php new file mode 100644 index 00000000..ff3ca376 --- /dev/null +++ b/vendor/doctrine/annotations/tests/Doctrine/Tests/Common/Annotations/TopLevelAnnotation.php @@ -0,0 +1,8 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Cache\\": "lib/" } + } +} 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 00000000..2d0cd23a --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ApcCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return apc_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return apc_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) apc_store($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return apc_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info(); + $sma = apc_sma_info(); + + return array( + 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_AVAILIABLE => $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 00000000..a7a70aad --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ArrayCache extends CacheProvider +{ + /** + * @var array $data + */ + private $data = array(); + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return (isset($this->data[$id])) ? $this->data[$id] : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return isset($this->data[$id]); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = $data; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return 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 00000000..d4e86f47 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return mixed The cached data or FALSE, if no cache entry exists for the given id. + */ + function fetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + function contains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param mixed $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + function delete($id); + + /** + * Retrieves cached information from data store + * + * The server's statistics array has the following values: + * + * - 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. + * + * @since 2.2 + * @return array Associative array with server's statistics if available, NULL otherwise. + */ + 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 00000000..4221a62e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base class for cache provider implementations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +abstract class CacheProvider implements Cache +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * @var string The namespace to prefix all cache ids with + */ + private $namespace = ''; + + /** + * @var string The namespace version + */ + private $namespaceVersion; + + /** + * Set the namespace to prefix all cache ids with. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + } + + /** + * Retrieve 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 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 delete($id) + { + return $this->doDelete($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function getStats() + { + return $this->doGetStats(); + } + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * Delete all cache entries. + * + * @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll() + { + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->getNamespaceVersion() + 1; + + $this->namespaceVersion = $namespaceVersion; + + return $this->doSave($namespaceCacheKey, $namespaceVersion); + } + + /** + * Prefix the passed id with the configured namespace value + * + * @param string $id The id to namespace + * @return string $id The namespaced id + */ + private function getNamespacedId($id) + { + $namespaceVersion = $this->getNamespaceVersion(); + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Namespace cache key + * + * @return string $namespaceCacheKey + */ + private function getNamespaceCacheKey() + { + return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + } + + /** + * Namespace version + * + * @return string $namespaceVersion + */ + private function getNamespaceVersion() + { + if (null !== $this->namespaceVersion) { + return $this->namespaceVersion; + } + + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->doFetch($namespaceCacheKey); + + if (false === $namespaceVersion) { + $namespaceVersion = 1; + + $this->doSave($namespaceCacheKey, $namespaceVersion); + } + + $this->namespaceVersion = $namespaceVersion; + + return $this->namespaceVersion; + } + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param bool|int $lifeTime The lifetime. If != false, sets a specific lifetime for this + * cache entry (null => infinite lifeTime). + * + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = false); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from data store + * + * @since 2.2 + * @return array 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/CouchbaseCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php new file mode 100644 index 00000000..f0e5f907 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Couchbase; + +/** + * Couchbase cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.4 + * @author Michael Nitschinger + */ +class CouchbaseCache extends CacheProvider +{ + + /** + * @var Couchbase + */ + private $couchbase; + + /** + * Sets the Couchbase instance to use. + * + * @param Couchbase $couchbase + */ + public function setCouchbase(Couchbase $couchbase) + { + $this->couchbase = $couchbase; + } + + /** + * Gets the Couchbase instance used by the cache. + * + * @return Couchbase + */ + public function getCouchbase() + { + return $this->couchbase; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->couchbase->get($id) ?: false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (null !== $this->couchbase->get($id)); + } + + /** + * {@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 array( + 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_AVAILIABLE => $stats['limit_maxbytes'], + ); + } + +} 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 00000000..da650b4c --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +abstract class FileCache extends CacheProvider +{ + /** + * @var string Cache directory. + */ + protected $directory; + + /** + * @var string Cache file extension. + */ + protected $extension; + + /** + * Constructor + * + * @param string $directory Cache directory. + * @param string $directory Cache file extension. + * + * @throws \InvalidArgumentException + */ + public function __construct($directory, $extension = null) + { + if ( ! is_dir($directory) && ! @mkdir($directory, 0777, true)) { + 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 + )); + } + + $this->directory = realpath($directory); + $this->extension = $extension ?: $this->extension; + } + + /** + * 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; + } + + /** + * @return string + */ + protected function getFilename($id) + { + $path = implode(str_split(md5($id), 12), DIRECTORY_SEPARATOR); + $path = $this->directory . DIRECTORY_SEPARATOR . $path; + + return $path . DIRECTORY_SEPARATOR . $id . $this->extension; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return @unlink($this->getFilename($id)); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $pattern = '/^.+\\' . $this->extension . '$/i'; + $iterator = new \RecursiveDirectoryIterator($this->directory); + $iterator = new \RecursiveIteratorIterator($iterator); + $iterator = new \RegexIterator($iterator, $pattern); + + foreach ($iterator as $name => $file) { + @unlink($name); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} \ No newline at end of file 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 00000000..a431438e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Filesystem cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class FilesystemCache extends FileCache +{ + const EXTENSION = '.doctrinecache.data'; + + /** + * {@inheritdoc} + */ + protected $extension = self::EXTENSION; + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $data = ''; + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (integer) $line; + } + + if ($lifetime !== 0 && $lifetime < time()) { + fclose($resource); + + return false; + } + + while (false !== ($line = fgets($resource))) { + $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"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (integer) $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); + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! is_dir($filepath)) { + mkdir($filepath, 0777, true); + } + + return file_put_contents($filename, $lifeTime . PHP_EOL . $data); + } +} \ No newline at end of file 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 00000000..5687b965 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcacheCache extends CacheProvider +{ + /** + * @var Memcache + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (bool) $this->memcache->get($id); + } + + /** + * {@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) + { + return $this->memcache->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcache->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcache->getStats(); + return array( + 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_AVAILIABLE => $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 00000000..75f13455 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcachedCache extends CacheProvider +{ + /** + * @var Memcached + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== $this->memcached->get($id)); + } + + /** + * {@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 doDelete($id) + { + return $this->memcached->delete($id); + } + + /** + * {@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 array( + 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_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} 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 00000000..1d69d3d6 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Php file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class PhpFileCache extends FileCache +{ + const EXTENSION = '.doctrinecache.php'; + + /** + * {@inheritdoc} + */ + protected $extension = self::EXTENSION; + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $value = include $filename; + + if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { + return false; + } + + return $value['data']; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $value = include $filename; + + return $value['lifetime'] === 0 || $value['lifetime'] > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + if (is_object($data) && ! method_exists($data, '__set_state')) { + throw new \InvalidArgumentException( + "Invalid argument given, PhpFileCache only allows objects that implement __set_state() " . + "and fully support var_export(). You can use the FilesystemCache to save arbitrary object " . + "graphs using serialize()/deserialize()." + ); + } + + $filename = $this->getFilename($id); + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! is_dir($filepath)) { + mkdir($filepath, 0777, true); + } + + $value = array( + 'lifetime' => $lifeTime, + 'data' => $data + ); + + $value = var_export($value, true); + $code = sprintf('. + */ + +namespace Doctrine\Common\Cache; + +use Redis; + +/** + * Redis cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Osman Ungur + */ +class RedisCache extends CacheProvider +{ + /** + * @var Redis + */ + private $redis; + + /** + * Sets the redis instance to use. + * + * @param Redis $redis + */ + public function setRedis(Redis $redis) + { + $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); + $this->redis = $redis; + } + + /** + * Gets the redis instance used by the cache. + * + * @return Redis + */ + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->redis->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $result = $this->redis->set($id, $data); + if ($lifeTime > 0) { + $this->redis->expire($id, $lifeTime); + } + return $result; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->redis->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->redis->info(); + return array( + Cache::STATS_HITS => false, + Cache::STATS_MISSES => false, + Cache::STATS_UPTIME => $info['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['used_memory'], + Cache::STATS_MEMORY_AVAILIABLE => false + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100644 index 00000000..777d0fd5 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class WinCacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return wincache_ucache_get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return wincache_ucache_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) wincache_ucache_set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = wincache_ucache_info(); + $meminfo = wincache_ucache_meminfo(); + + return array( + Cache::STATS_HITS => $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_AVAILIABLE => $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 00000000..8733e266 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class XcacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->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, 0); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On + * @return void + */ + 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 array( + Cache::STATS_HITS => $info['hits'], + Cache::STATS_MISSES => $info['misses'], + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $info['size'], + Cache::STATS_MEMORY_AVAILIABLE => $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 00000000..fc90bc69 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Ralph Schindler + * @author Guilherme Blanco + */ +class ZendDataCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return zend_shm_cache_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== zend_shm_cache_fetch($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return zend_shm_cache_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return zend_shm_cache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $namespace = $this->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/cache/phpunit.xml.dist b/vendor/doctrine/cache/phpunit.xml.dist new file mode 100644 index 00000000..900378bf --- /dev/null +++ b/vendor/doctrine/cache/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php new file mode 100644 index 00000000..df812620 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php @@ -0,0 +1,20 @@ +markTestSkipped('The ' . __CLASS__ .' requires the use of APC'); + } + } + + protected function _getCacheDriver() + { + return new ApcCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php new file mode 100644 index 00000000..6cad8915 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php @@ -0,0 +1,21 @@ +_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php new file mode 100644 index 00000000..ea7f7537 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php @@ -0,0 +1,103 @@ +_getCacheDriver(); + + // Test save + $cache->save('test_key', 'testing this out'); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key')); + + // Test fetch + $this->assertEquals('testing this out', $cache->fetch('test_key')); + + // Test delete + $cache->save('test_key2', 'test2'); + $cache->delete('test_key2'); + $this->assertFalse($cache->contains('test_key2')); + } + + public function testObjects() + { + $cache = $this->_getCacheDriver(); + + // Fetch/save test with objects (Is cache driver serializes/unserializes objects correctly ?) + $cache->save('test_object_key', new \ArrayObject()); + $this->assertTrue($cache->fetch('test_object_key') instanceof \ArrayObject); + } + + public function testDeleteAll() + { + $cache = $this->_getCacheDriver(); + $cache->save('test_key1', '1'); + $cache->save('test_key2', '2'); + $cache->deleteAll(); + + $this->assertFalse($cache->contains('test_key1')); + $this->assertFalse($cache->contains('test_key2')); + } + + public function testFlushAll() + { + $cache = $this->_getCacheDriver(); + $cache->save('test_key1', '1'); + $cache->save('test_key2', '2'); + $cache->flushAll(); + + $this->assertFalse($cache->contains('test_key1')); + $this->assertFalse($cache->contains('test_key2')); + } + + public function testNamespace() + { + $cache = $this->_getCacheDriver(); + $cache->setNamespace('test_'); + $cache->save('key1', 'test'); + + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('test2_'); + + $this->assertFalse($cache->contains('key1')); + } + + /** + * @group DCOM-43 + */ + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertArrayHasKey(Cache::STATS_HITS, $stats); + $this->assertArrayHasKey(Cache::STATS_MISSES, $stats); + $this->assertArrayHasKey(Cache::STATS_UPTIME, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILIABLE, $stats); + } + + /** + * Make sure that all supported caches return "false" instead of "null" to be compatible + * with ORM integration. + */ + public function testFalseOnFailedFetch() + { + $cache = $this->_getCacheDriver(); + $result = $cache->fetch('nonexistent_key'); + $this->assertFalse($result); + $this->assertNotNull($result); + } + + /** + * @return \Doctrine\Common\Cache\CacheProvider + */ + abstract protected function _getCacheDriver(); +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php new file mode 100644 index 00000000..40d5a693 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php @@ -0,0 +1,47 @@ +couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default'); + } catch(Exception $ex) { + $this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex); + } + } else { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of the couchbase extension'); + } + } + + public function testNoExpire() + { + $cache = $this->_getCacheDriver(); + $cache->save('noexpire', 'value', 0); + sleep(1); + $this->assertTrue($cache->contains('noexpire'), 'Couchbase provider should support no-expire'); + } + + public function testLongLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('key', 'value', 30 * 24 * 3600 + 1); + + $this->assertTrue($cache->contains('key'), 'Couchbase provider should support TTL > 30 days'); + } + + protected function _getCacheDriver() + { + $driver = new CouchbaseCache(); + $driver->setCouchbase($this->couchbase); + return $driver; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php new file mode 100644 index 00000000..f782e3c0 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php @@ -0,0 +1,97 @@ +assertFalse(is_dir($dir)); + + $this->driver = new FilesystemCache($dir); + $this->assertTrue(is_dir($dir)); + + return $this->driver; + } + + public function testLifetime() + { + $cache = $this->_getCacheDriver(); + + // Test save + $cache->save('test_key', 'testing this out', 10); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key')); + + // Test fetch + $this->assertEquals('testing this out', $cache->fetch('test_key')); + + // access private methods + $getFilename = new \ReflectionMethod($cache, 'getFilename'); + $getNamespacedId = new \ReflectionMethod($cache, 'getNamespacedId'); + + $getFilename->setAccessible(true); + $getNamespacedId->setAccessible(true); + + $id = $getNamespacedId->invoke($cache, 'test_key'); + $filename = $getFilename->invoke($cache, $id); + + $data = ''; + $lifetime = 0; + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (integer) $line; + } + + while (false !== ($line = fgets($resource))) { + $data .= $line; + } + + $this->assertNotEquals(0, $lifetime, "previous lifetime could not be loaded"); + + // update lifetime + $lifetime = $lifetime - 20; + file_put_contents($filename, $lifetime . PHP_EOL . $data); + + // test expired data + $this->assertFalse($cache->contains('test_key')); + $this->assertFalse($cache->fetch('test_key')); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + public function tearDown() + { + $dir = $this->driver->getDirectory(); + $ext = $this->driver->getExtension(); + $iterator = new \RecursiveDirectoryIterator($dir); + + foreach (new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST) as $file) { + if ($file->isFile()) { + @unlink($file->getRealPath()); + } else { + @rmdir($file->getRealPath()); + } + } + } + +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php new file mode 100644 index 00000000..36c180c9 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php @@ -0,0 +1,45 @@ +_memcache = new \Memcache; + $ok = @$this->_memcache->connect('localhost', 11211); + if (!$ok) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); + } + } else { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); + } + } + + public function testNoExpire() { + $cache = $this->_getCacheDriver(); + $cache->save('noexpire', 'value', 0); + sleep(1); + $this->assertTrue($cache->contains('noexpire'), 'Memcache provider should support no-expire'); + } + + public function testLongLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('key', 'value', 30 * 24 * 3600 + 1); + $this->assertTrue($cache->contains('key'), 'Memcache provider should support TTL > 30 days'); + } + + protected function _getCacheDriver() + { + $driver = new MemcacheCache(); + $driver->setMemcache($this->_memcache); + return $driver; + } + +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php new file mode 100644 index 00000000..ecbe5a60 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php @@ -0,0 +1,48 @@ +memcached = new \Memcached(); + $this->memcached->setOption(\Memcached::OPT_COMPRESSION, false); + $this->memcached->addServer('127.0.0.1', 11211); + + $fh = @fsockopen('127.0.0.1', 11211); + if (!$fh) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); + } + } else { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); + } + } + + public function testNoExpire() { + $cache = $this->_getCacheDriver(); + $cache->save('noexpire', 'value', 0); + sleep(1); + $this->assertTrue($cache->contains('noexpire'), 'Memcache provider should support no-expire'); + } + + public function testLongLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('key', 'value', 30 * 24 * 3600 + 1); + + $this->assertTrue($cache->contains('key'), 'Memcached provider should support TTL > 30 days'); + } + + protected function _getCacheDriver() + { + $driver = new MemcachedCache(); + $driver->setMemcached($this->memcached); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php new file mode 100644 index 00000000..5085f466 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php @@ -0,0 +1,149 @@ +assertFalse(is_dir($dir)); + + $this->driver = new PhpFileCache($dir); + $this->assertTrue(is_dir($dir)); + + return $this->driver; + } + + public function testObjects() + { + $this->markTestSkipped('PhpFileCache does not support saving objects that dont implement __set_state()'); + } + + public function testLifetime() + { + $cache = $this->_getCacheDriver(); + + // Test save + $cache->save('test_key', 'testing this out', 10); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key')); + + // Test fetch + $this->assertEquals('testing this out', $cache->fetch('test_key')); + + // access private methods + $getFilename = new \ReflectionMethod($cache, 'getFilename'); + $getNamespacedId = new \ReflectionMethod($cache, 'getNamespacedId'); + + $getFilename->setAccessible(true); + $getNamespacedId->setAccessible(true); + + $id = $getNamespacedId->invoke($cache, 'test_key'); + $path = $getFilename->invoke($cache, $id); + $value = include $path; + + // update lifetime + $value['lifetime'] = $value['lifetime'] - 20; + file_put_contents($path, 'assertFalse($cache->contains('test_key')); + $this->assertFalse($cache->fetch('test_key')); + } + + public function testImplementsSetState() + { + $cache = $this->_getCacheDriver(); + + // Test save + $cache->save('test_set_state', new SetStateClass(array(1,2,3))); + + //Test __set_state call + $this->assertCount(0, SetStateClass::$values); + + // Test fetch + $value = $cache->fetch('test_set_state'); + $this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value); + $this->assertEquals(array(1,2,3), $value->getValue()); + + //Test __set_state call + $this->assertCount(1, SetStateClass::$values); + + // Test contains + $this->assertTrue($cache->contains('test_set_state')); + } + + public function testNotImplementsSetState() + { + $cache = $this->_getCacheDriver(); + + $this->setExpectedException('InvalidArgumentException'); + $cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3))); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + public function tearDown() + { + if (!$this->driver) { + return; + } + + $dir = $this->driver->getDirectory(); + $ext = $this->driver->getExtension(); + $iterator = new \RecursiveDirectoryIterator($dir); + + foreach (new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST) as $file) { + if ($file->isFile()) { + @unlink($file->getRealPath()); + } else { + @rmdir($file->getRealPath()); + } + } + } + +} + +class NotSetStateClass +{ + private $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } +} + +class SetStateClass extends NotSetStateClass +{ + public static $values = array(); + + public static function __set_state($data) + { + self::$values = $data; + return new self($data['value']); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php new file mode 100644 index 00000000..45bbc752 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php @@ -0,0 +1,30 @@ +_redis = new \Redis(); + $ok = @$this->_redis->connect('127.0.0.1'); + if (!$ok) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of redis'); + } + } else { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of redis'); + } + } + + protected function _getCacheDriver() + { + $driver = new RedisCache(); + $driver->setRedis($this->_redis); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php new file mode 100644 index 00000000..cb363df9 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php @@ -0,0 +1,20 @@ +markTestSkipped('The ' . __CLASS__ .' requires the use of Wincache'); + } + } + + protected function _getCacheDriver() + { + return new WincacheCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/XcacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/XcacheCacheTest.php new file mode 100644 index 00000000..62598487 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/XcacheCacheTest.php @@ -0,0 +1,20 @@ +markTestSkipped('The ' . __CLASS__ .' requires the use of xcache'); + } + } + + protected function _getCacheDriver() + { + return new XcacheCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ZendDataCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ZendDataCacheTest.php new file mode 100644 index 00000000..cd66e157 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ZendDataCacheTest.php @@ -0,0 +1,28 @@ +markTestSkipped('The ' . __CLASS__ .' requires the use of Zend Data Cache which only works in apache2handler SAPI'); + } + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + protected function _getCacheDriver() + { + return new ZendDataCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 00000000..e8323d29 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} 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 00000000..9c2c8e14 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,385 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, ArrayIterator; +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; + +/** + * An ArrayCollection is a Collection implementation that wraps a regular PHP array. + * + * @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 = array()) + { + $this->_elements = $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)) { + $removed = $this->_elements[$key]; + unset($this->_elements[$key]); + + return $removed; + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $key = array_search($element, $this->_elements, true); + + if ($key !== false) { + unset($this->_elements[$key]); + + return true; + } + + return false; + } + + /** + * 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)) { + return $this->add($value); + } + return $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + return $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) + { + if (isset($this->_elements[$key])) { + return $this->_elements[$key]; + } + return 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($value) + { + $this->_elements[] = $value; + return true; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return ! $this->_elements; + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + return new static(array_map($func, $this->_elements)); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + return new static(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) + { + $coll1 = $coll2 = array(); + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + $coll1[$key] = $element; + } else { + $coll2[$key] = $element; + } + } + return array(new static($coll1), new static($coll2)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->_elements = array(); + } + + /** + * {@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 == 'DESC' ? -1 : 1, $next); + } + + usort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return new static($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 00000000..0edf054a --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,261 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, Countable, IteratorAggregate, ArrayAccess; + +/** + * 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 preferrable. + * 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 boolean Always TRUE. + */ + function add($element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + 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 boolean TRUE if the collection contains the element, FALSE otherwise. + */ + function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + function isEmpty(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|integer $key The kex/index of the element to remove. + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + */ + function remove($key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * + * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. + */ + function removeElement($element); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|integer $key The key/index to check for. + * + * @return boolean TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + function containsKey($key); + + /** + * Gets the element at the specified key/index. + * + * @param string|integer $key The key/index of the element to retrieve. + * + * @return mixed + */ + 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. + */ + 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. + */ + function getValues(); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|integer $key The key/index of the element to set. + * @param mixed $value The element to set. + * + * @return void + */ + function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + */ + function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + */ + function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string + */ + function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + */ + function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + */ + function next(); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + 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. + */ + function filter(Closure $p); + + /** + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + 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 + */ + 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 array 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. + */ + 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. + */ + 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 + */ + 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 00000000..42929bdc --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,245 @@ +. + */ + +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 array|null + */ + 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 array|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->orderings = $orderings; + $this->firstResult = $firstResult; + $this->maxResults = $maxResults; + } + + /** + * 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, array( + $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, array( + $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 array + */ + 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 array $orderings + * + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = $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 = $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 = $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 00000000..78a0755a --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,224 @@ +. + */ + +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 $object + * @param string $field + * + * @return mixed + */ + public static function getObjectFieldValue($object, $field) + { + $accessors = array('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 || is_array($object)) { + return $object[$field]; + } + + 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() { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) { + $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: + case Comparison::IS: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) { + return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) { + return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $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 = array(); + + 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($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + return true; + }; + } + + /** + * @param array $expressions + * + * @return callable + */ + private function orExpressions($expressions) + { + return function ($object) use ($expressions) { + 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 00000000..641feecf --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,103 @@ +. + */ + +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 = 'IS'; + const IN = 'IN'; + const NIN = 'NIN'; + const CONTAINS = 'CONTAINS'; + + /** + * @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 00000000..3613c027 --- /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 = array(); + + /** + * @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 00000000..68db767c --- /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 00000000..080afdc6 --- /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 00000000..7f6e8314 --- /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 00000000..2bd7eaab --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,162 @@ +. + */ + +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. + * + * @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::IS, 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)); + } +} 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 00000000..401d46e4 --- /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 + */ + function matching(Criteria $criteria); +} diff --git a/vendor/doctrine/collections/phpunit.xml.dist b/vendor/doctrine/collections/phpunit.xml.dist new file mode 100644 index 00000000..36968e99 --- /dev/null +++ b/vendor/doctrine/collections/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php new file mode 100644 index 00000000..b640043e --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\Tests\Common\Collections; + +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; +use Doctrine\Common\Collections\ExpressionBuilder; + +/** + * @group DDC-1637 + */ +class ClosureExpressionVisitorTest extends \PHPUnit_Framework_TestCase +{ + private $visitor; + private $builder; + + public function setUp() + { + $this->visitor = new ClosureExpressionVisitor(); + $this->builder = new ExpressionBuilder(); + } + + public function testGetObjectFieldValueIsAccessor() + { + $object = new TestObject(1, 2, true); + + $this->assertTrue($this->visitor->getObjectFieldValue($object, 'baz')); + } + + public function testGetObjectFieldValueMagicCallMethod() + { + $object = new TestObject(1, 2, true, 3); + + $this->assertEquals(3, $this->visitor->getObjectFieldValue($object, 'qux')); + } + + public function testWalkEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 1)); + + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + } + + public function testWalkNotEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->neq("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(2))); + } + + public function testWalkLessThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lt("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkLessThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lte("foo", 1)); + + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkGreaterThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gte("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkGreaterThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gt("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->in("foo", array(1, 2, 3))); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkNotInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->notIn("foo", array(1, 2, 3))); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(0))); + $this->assertTrue($closure(new TestObject(4))); + } + + public function testWalkContainsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->contains('foo', 'hello')); + + $this->assertTrue($closure(new TestObject('hello world'))); + $this->assertFalse($closure(new TestObject('world'))); + } + + public function testWalkAndCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->andX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertFalse($closure(new TestObject(1, 0))); + $this->assertFalse($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testWalkOrCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->orX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertTrue($closure(new TestObject(1, 0))); + $this->assertTrue($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testSortByFieldAscending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo"); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("c", $objects[2]->getFoo()); + } + + public function testSortByFieldDescending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo", -1); + + usort($objects, $sort); + + $this->assertEquals("c", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("a", $objects[2]->getFoo()); + } + + public function testSortDelegate() + { + $objects = array(new TestObject("a", "c"), new TestObject("a", "b"), new TestObject("a", "a")); + $sort = ClosureExpressionVisitor::sortByField("bar", 1); + $sort = ClosureExpressionVisitor::sortByField("foo", 1, $sort); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getBar()); + $this->assertEquals("b", $objects[1]->getBar()); + $this->assertEquals("c", $objects[2]->getBar()); + } + + public function testArrayComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 42)); + + $this->assertTrue($closure(array('foo' => 42))); + } +} + +class TestObject +{ + private $foo; + private $bar; + private $baz; + private $qux; + + public function __construct($foo = null, $bar = null, $baz = null, $qux = null) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + $this->qux = $qux; + } + + public function __call($name, $arguments) + { + if ('getqux' === $name) { + return $this->qux; + } + } + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } + + public function isBaz() + { + return $this->baz; + } +} + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php new file mode 100644 index 00000000..d98246c4 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php @@ -0,0 +1,264 @@ +_coll = new \Doctrine\Common\Collections\ArrayCollection; + } + + public function testIssetAndUnset() + { + $this->assertFalse(isset($this->_coll[0])); + $this->_coll->add('testing'); + $this->assertTrue(isset($this->_coll[0])); + unset($this->_coll[0]); + $this->assertFalse(isset($this->_coll[0])); + } + + public function testToString() + { + $this->_coll->add('testing'); + $this->assertTrue(is_string((string) $this->_coll)); + } + + public function testRemovingNonExistentEntryReturnsNull() + { + $this->assertEquals(null, $this->_coll->remove('testing_does_not_exist')); + } + + public function testExists() + { + $this->_coll->add("one"); + $this->_coll->add("two"); + $exists = $this->_coll->exists(function($k, $e) { return $e == "one"; }); + $this->assertTrue($exists); + $exists = $this->_coll->exists(function($k, $e) { return $e == "other"; }); + $this->assertFalse($exists); + } + + public function testMap() + { + $this->_coll->add(1); + $this->_coll->add(2); + $res = $this->_coll->map(function($e) { return $e * 2; }); + $this->assertEquals(array(2, 4), $res->toArray()); + } + + public function testFilter() + { + $this->_coll->add(1); + $this->_coll->add("foo"); + $this->_coll->add(3); + $res = $this->_coll->filter(function($e) { return is_numeric($e); }); + $this->assertEquals(array(0 => 1, 2 => 3), $res->toArray()); + } + + public function testFirstAndLast() + { + $this->_coll->add('one'); + $this->_coll->add('two'); + + $this->assertEquals($this->_coll->first(), 'one'); + $this->assertEquals($this->_coll->last(), 'two'); + } + + public function testArrayAccess() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + + $this->assertEquals($this->_coll[0], 'one'); + $this->assertEquals($this->_coll[1], 'two'); + + unset($this->_coll[0]); + $this->assertEquals($this->_coll->count(), 1); + } + + public function testContainsKey() + { + $this->_coll[5] = 'five'; + $this->assertTrue($this->_coll->containsKey(5)); + } + + public function testContains() + { + $this->_coll[0] = 'test'; + $this->assertTrue($this->_coll->contains('test')); + } + + public function testSearch() + { + $this->_coll[0] = 'test'; + $this->assertEquals(0, $this->_coll->indexOf('test')); + } + + public function testGet() + { + $this->_coll[0] = 'test'; + $this->assertEquals('test', $this->_coll->get(0)); + } + + public function testGetKeys() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->assertEquals(array(0, 1), $this->_coll->getKeys()); + } + + public function testGetValues() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->assertEquals(array('one', 'two'), $this->_coll->getValues()); + } + + public function testCount() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->assertEquals($this->_coll->count(), 2); + $this->assertEquals(count($this->_coll), 2); + } + + public function testForAll() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->assertEquals($this->_coll->forAll(function($k, $e) { return is_string($e); }), true); + $this->assertEquals($this->_coll->forAll(function($k, $e) { return is_array($e); }), false); + } + + public function testPartition() + { + $this->_coll[] = true; + $this->_coll[] = false; + $partition = $this->_coll->partition(function($k, $e) { return $e == true; }); + $this->assertEquals($partition[0][0], true); + $this->assertEquals($partition[1][0], false); + } + + public function testClear() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->_coll->clear(); + $this->assertEquals($this->_coll->isEmpty(), true); + } + + public function testRemove() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $el = $this->_coll->remove(0); + + $this->assertEquals('one', $el); + $this->assertEquals($this->_coll->contains('one'), false); + $this->assertNull($this->_coll->remove(0)); + } + + public function testRemoveElement() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + + $this->assertTrue($this->_coll->removeElement('two')); + $this->assertFalse($this->_coll->contains('two')); + $this->assertFalse($this->_coll->removeElement('two')); + } + + public function testSlice() + { + $this->_coll[] = 'one'; + $this->_coll[] = 'two'; + $this->_coll[] = 'three'; + + $slice = $this->_coll->slice(0, 1); + $this->assertInternalType('array', $slice); + $this->assertEquals(array('one'), $slice); + + $slice = $this->_coll->slice(1); + $this->assertEquals(array(1 => 'two', 2 => 'three'), $slice); + + $slice = $this->_coll->slice(1, 1); + $this->assertEquals(array(1 => 'two'), $slice); + } + + public function fillMatchingFixture() + { + $std1 = new \stdClass(); + $std1->foo = "bar"; + $this->_coll[] = $std1; + + $std2 = new \stdClass(); + $std2->foo = "baz"; + $this->_coll[] = $std2; + } + + /** + * @group DDC-1637 + */ + public function testMatching() + { + $this->fillMatchingFixture(); + + $col = $this->_coll->matching(new Criteria(Criteria::expr()->eq("foo", "bar"))); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->_coll); + $this->assertEquals(1, count($col)); + } + + /** + * @group DDC-1637 + */ + public function testMatchingOrdering() + { + $this->fillMatchingFixture(); + + $col = $this->_coll->matching(new Criteria(null, array('foo' => 'DESC'))); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->_coll); + $this->assertEquals(2, count($col)); + $this->assertEquals('baz', $col[0]->foo); + $this->assertEquals('bar', $col[1]->foo); + } + + /** + * @group DDC-1637 + */ + public function testMatchingSlice() + { + $this->fillMatchingFixture(); + + $col = $this->_coll->matching(new Criteria(null, null, 1, 1)); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->_coll); + $this->assertEquals(1, count($col)); + $this->assertEquals('baz', $col[0]->foo); + } + + public function testCanRemoveNullValuesByKey() + { + $this->_coll->add(null); + $this->_coll->remove(0); + $this->assertTrue($this->_coll->isEmpty()); + } + + public function testCanVerifyExistingKeysWithNullValues() + { + $this->_coll->set('key', null); + $this->assertTrue($this->_coll->containsKey('key')); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php new file mode 100644 index 00000000..03fb6cae --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php @@ -0,0 +1,82 @@ +assertInstanceOf("Doctrine\Common\Collections\Criteria", $criteria); + } + + public function testConstructor() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria($expr, array("foo" => "ASC"), 10, 20); + + $this->assertSame($expr, $criteria->getWhereExpression()); + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + $this->assertEquals(10, $criteria->getFirstResult()); + $this->assertEquals(20, $criteria->getMaxResults()); + } + + public function testWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + + $this->assertSame($expr, $criteria->getWhereExpression()); + } + + public function testAndWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->andWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_AND, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->orWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_OR, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrderings() + { + $criteria = Criteria::create() + ->orderBy(array("foo" => "ASC")); + + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + } + + public function testExpr() + { + $this->assertInstanceOf('Doctrine\Common\Collections\ExpressionBuilder', Criteria::expr()); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php new file mode 100644 index 00000000..23dfc217 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php @@ -0,0 +1,122 @@ +builder = new ExpressionBuilder(); + } + + public function testAndX() + { + $expr = $this->builder->andX($this->builder->eq("a", "b")); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\CompositeExpression", $expr); + $this->assertEquals(CompositeExpression::TYPE_AND, $expr->getType()); + } + + public function testOrX() + { + $expr = $this->builder->orX($this->builder->eq("a", "b")); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\CompositeExpression", $expr); + $this->assertEquals(CompositeExpression::TYPE_OR, $expr->getType()); + } + + public function testInvalidAndXArgument() + { + $this->setExpectedException("RuntimeException"); + $this->builder->andX("foo"); + } + + public function testEq() + { + $expr = $this->builder->eq("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::EQ, $expr->getOperator()); + } + + public function testNeq() + { + $expr = $this->builder->neq("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::NEQ, $expr->getOperator()); + } + + public function testLt() + { + $expr = $this->builder->lt("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::LT, $expr->getOperator()); + } + + public function testGt() + { + $expr = $this->builder->gt("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::GT, $expr->getOperator()); + } + + public function testGte() + { + $expr = $this->builder->gte("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::GTE, $expr->getOperator()); + } + + public function testLte() + { + $expr = $this->builder->lte("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::LTE, $expr->getOperator()); + } + + public function testIn() + { + $expr = $this->builder->in("a", array("b")); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::IN, $expr->getOperator()); + } + + public function testNotIn() + { + $expr = $this->builder->notIn("a", array("b")); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::NIN, $expr->getOperator()); + } + + public function testIsNull() + { + $expr = $this->builder->isNull("a"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::IS, $expr->getOperator()); + } + + public function testContains() + { + $expr = $this->builder->contains("a", "b"); + + $this->assertInstanceOf("Doctrine\Common\Collections\Expr\Comparison", $expr); + $this->assertEquals(Comparison::CONTAINS, $expr->getOperator()); + } +} + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/collections/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 00000000..e8323d29 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ + - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - +Copyright (c) 2006-2012 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/bin/travis-setup.php b/vendor/doctrine/common/bin/travis-setup.php new file mode 100644 index 00000000..e9c355ae --- /dev/null +++ b/vendor/doctrine/common/bin/travis-setup.php @@ -0,0 +1,141 @@ +. + */ + +/** + * Install PHP extensions required for testing by Travis CI. + * + * @author Victor Berchet + * @since 2.2 + */ +$installer = new PhpExtensions(); + +if (isset($argv[1]) && 'APC' === strtoupper($argv[1])) { + $installer->install('apc'); +} else { + $installer->install('xcache'); +} + +$installer->install('memcache'); +$installer->install('memcached'); + +class PhpExtensions +{ + protected $extensions; + protected $phpVersion; + protected $iniPath; + + public function __construct() + { + $this->phpVersion = phpversion(); + $this->iniPath = php_ini_loaded_file(); + $this->extensions = array( + 'memcache' => array( + 'url' => 'http://pecl.php.net/get/memcache-2.2.6.tgz', + 'php_version' => array(), + 'cfg' => array('--enable-memcache'), + 'ini' => array('extension=memcache.so'), + ), + 'memcached' => array( + 'url' => 'http://pecl.php.net/get/memcached-1.0.2.tgz', + 'php_version' => array( + // memcached 1.0.2 does not build on PHP 5.4 + array('<', '5.4'), + ), + 'cfg' => array(), + 'ini' => array('extension=memcached.so'), + ), + 'apc' => array( + 'url' => 'http://pecl.php.net/get/APC-3.1.9.tgz', + 'php_version' => array( + // apc 3.1.9 causes a segfault on PHP 5.4 + array('<', '5.4'), + ), + 'cfg' => array(), + 'ini' => array( + 'extension=apc.so', + 'apc.enabled=1', + 'apc.enable_cli=1' + ), + ), + 'xcache' => array( + 'url' => 'http://xcache.lighttpd.net/pub/Releases/1.2.2/xcache-1.2.2.tar.gz', + 'php_version' => array( + // xcache does not build with Travis CI (as of 2012-01-09) + array('<', '5'), + ), + 'cfg' => array('--enable-xcache'), + 'ini' => array( + 'extension=xcache.so', + 'xcache.cacher=false', + 'xcache.admin.enable_auth=0', + 'xcache.var_size=1M', + ), + ), + ); + } + + public function install($name) + { + if (array_key_exists($name, $this->extensions)) { + $extension = $this->extensions[$name]; + + + echo "== extension: $name ==\n"; + + foreach ($extension['php_version'] as $version) { + if (!version_compare($this->phpVersion, $version[1], $version[0])) { + printf( + "=> not installed, requires a PHP version %s %s (%s installed)\n", + $version[0], + $version[1], + $this->phpVersion + ); + + return; + } + } + + $this->system(sprintf("wget %s > /dev/null 2>&1", $extension['url'])); + $file = basename($extension['url']); + $this->system(sprintf("tar -xzf %s > /dev/null 2>&1", $file)); + $folder = basename($file, ".tgz"); + $folder = basename($folder, ".tar.gz"); + $this->system(sprintf( + 'sh -c "cd %s && phpize && ./configure %s && make && sudo make install" > /dev/null 2>&1', + $folder, + implode(' ', $extension['cfg']) + )); + foreach ($extension['ini'] as $ini) { + $this->system(sprintf("echo %s >> %s", $ini, $this->iniPath)); + } + printf("=> installed (%s)\n", $folder); + } + } + + private function system($cmd) + { + $ret = 0; + system($cmd, $ret); + if (0 !== $ret) { + printf("=> Command '%s' failed !", $cmd); + + exit($ret); + } + } +} diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json index f995f6c6..2fba7b78 100644 --- a/vendor/doctrine/common/composer.json +++ b/vendor/doctrine/common/composer.json @@ -1,10 +1,10 @@ { "name": "doctrine/common", - "type": "library","version":"2.2.3", + "type": "library","version":"2.4.0-RC3", "description": "Common Library for Doctrine projects", "keywords": ["collections", "spl", "eventmanager", "annotations", "persistence"], "homepage": "http://www.doctrine-project.org", - "license": "LGPL", + "license": "MIT", "authors": [ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, {"name": "Roman Borschel", "email": "roman@code-factory.org"}, @@ -13,9 +13,19 @@ {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} ], "require": { - "php": ">=5.3.2" + "php": ">=5.3.2", + "doctrine/inflector": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/lexer": "1.*", + "doctrine/annotations": "1.*" }, "autoload": { - "psr-0": { "Doctrine\\Common": "lib/" } + "psr-0": { "Doctrine\\Common\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } } } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php index 375b0d6b..86faf433 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -149,7 +149,7 @@ public function unregister() /** * Loads the given class or interface. * - * @param string $classname The name of the class to load. + * @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) @@ -181,7 +181,7 @@ public function canLoadClass($className) $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; if ($this->includePath !== null) { - return file_exists($this->includePath . DIRECTORY_SEPARATOR . $file); + return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); } return (false !== stream_resolve_include_path($file)); @@ -234,9 +234,13 @@ public static function classExists($className) } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" return true; } + + if (class_exists($className, false) || interface_exists($className, false)) { + return true; + } } - return class_exists($className, false) || interface_exists($className, false); + return false; } /** @@ -244,7 +248,7 @@ public static function classExists($className) * for (and is able to load) the class with the given name. * * @param string $className The name of the class. - * @return The ClassLoader for the class or NULL if no such ClassLoader exists. + * @return ClassLoader The ClassLoader for the class or NULL if no such ClassLoader exists. */ public static function getClassLoader($className) { diff --git a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php index 8c5669be..6db76759 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -25,4 +25,4 @@ * */ class CommonException extends \Exception { -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php index bc95d303..20d065e7 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -40,6 +40,8 @@ interface Comparable * 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/EventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php index 6a3c0699..a87eee89 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php @@ -15,7 +15,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -40,7 +40,6 @@ class EventArgs { /** * @var EventArgs Single instance of EventArgs - * @static */ private static $_emptyEventArgsInstance; @@ -55,7 +54,6 @@ class EventArgs * * @see EventManager::dispatchEvent * @link http://msdn.microsoft.com/en-us/library/system.eventargs.aspx - * @static * @return EventArgs */ public static function getEmptyInstance() diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php index a1f11ed2..1e57df23 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php @@ -15,7 +15,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -127,10 +127,21 @@ public function removeEventListener($events, $listener) * Adds an EventSubscriber. The subscriber is asked for all the events he is * interested in and added as a listener for these events. * - * @param Doctrine\Common\EventSubscriber $subscriber The subscriber. + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. */ public function addEventSubscriber(EventSubscriber $subscriber) { $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); } -} \ No newline at end of file + + /** + * Removes an EventSubscriber. The subscriber is asked for all the events it is + * interested in and removed as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + */ + public function removeEventSubscriber(EventSubscriber $subscriber) + { + $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php index ed3383fa..d7b37c05 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php @@ -15,7 +15,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -41,5 +41,5 @@ interface EventSubscriber * * @return array */ - function getSubscribedEvents(); + public function getSubscribedEvents(); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php index 8df3b843..65129ab7 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -13,254 +13,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ namespace Doctrine\Common; +use Doctrine\Common\Lexer\AbstractLexer; + /** * Base class for writing simple lexers, i.e. for creating small DSLs. * + * Lexer moved into its own Component Doctrine\Common\Lexer. This class + * only stays for being BC. + * * @since 2.0 * @author Guilherme Blanco * @author Jonathan Wage * @author Roman Borschel - * @todo Rename: AbstractLexer */ -abstract class Lexer +abstract class Lexer extends AbstractLexer { - /** - * @var array Array of scanned tokens - */ - private $tokens = array(); - - /** - * @var integer Current lexer position in input string - */ - private $position = 0; - - /** - * @var integer Current peek of current lexer position - */ - private $peek = 0; - - /** - * @var array The next token in the input. - */ - public $lookahead; - - /** - * @var array The last matched/seen token. - */ - 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. - */ - public function setInput($input) - { - $this->tokens = array(); - $this->reset(); - $this->scan($input); - } - - /** - * Resets the lexer. - */ - public function reset() - { - $this->lookahead = null; - $this->token = null; - $this->peek = 0; - $this->position = 0; - } - - /** - * Resets the peek pointer to 0. - */ - 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 - */ - public function resetPosition($position = 0) - { - $this->position = $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. - * - * A 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 - * - * @return array|null the next token; null if there is no more tokens left - */ - 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 $type The token type to skip until. - */ - 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 - */ - protected function scan($input) - { - static $regex; - - if ( ! isset($regex)) { - $regex = '/(' . implode(')|(', $this->getCatchablePatterns()) . ')|' - . implode('|', $this->getNonCatchablePatterns()) . '/i'; - } - - $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; - } - - /** - * 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); -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php index 93e504ae..569b801d 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -15,7 +15,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -40,6 +40,5 @@ interface NotifyPropertyChanged * * @param PropertyChangedListener $listener */ - function addPropertyChangedListener(PropertyChangedListener $listener); + public function addPropertyChangedListener(PropertyChangedListener $listener); } - diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php index 9fff343c..94fcd052 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -14,7 +14,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -34,13 +34,46 @@ */ abstract class AbstractManagerRegistry implements ManagerRegistry { + /** + * @var string + */ private $name; + + /** + * @var array + */ private $connections; + + /** + * @var array + */ private $managers; + + /** + * @var string + */ private $defaultConnection; + + /** + * @var string + */ private $defaultManager; + + /** + * @var string + */ private $proxyInterfaceName; + /** + * Constructor + * + * @param string $name + * @param array $connections + * @param array $managers + * @param string $defaultConnection + * @param string $defaultManager + * @param string $proxyInterfaceName + */ public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName) { $this->name = $name; @@ -82,7 +115,7 @@ public function getName() } /** - * @inheritdoc + * {@inheritdoc} */ public function getConnection($name = null) { @@ -98,7 +131,7 @@ public function getConnection($name = null) } /** - * @inheritdoc + * {@inheritdoc} */ public function getConnectionNames() { @@ -106,7 +139,7 @@ public function getConnectionNames() } /** - * @inheritdoc + * {@inheritdoc} */ public function getConnections() { @@ -119,7 +152,7 @@ public function getConnections() } /** - * @inheritdoc + * {@inheritdoc} */ public function getDefaultConnectionName() { @@ -127,7 +160,7 @@ public function getDefaultConnectionName() } /** - * @inheritdoc + * {@inheritdoc} */ public function getDefaultManagerName() { @@ -135,7 +168,9 @@ public function getDefaultManagerName() } /** - * @inheritdoc + * {@inheritdoc} + * + * @throws \InvalidArgumentException */ public function getManager($name = null) { @@ -151,7 +186,7 @@ public function getManager($name = null) } /** - * @inheritdoc + * {@inheritdoc} */ public function getManagerForClass($class) { @@ -176,7 +211,7 @@ public function getManagerForClass($class) } /** - * @inheritdoc + * {@inheritdoc} */ public function getManagerNames() { @@ -184,7 +219,7 @@ public function getManagerNames() } /** - * @inheritdoc + * {@inheritdoc} */ public function getManagers() { @@ -197,7 +232,7 @@ public function getManagers() } /** - * @inheritdoc + * {@inheritdoc} */ public function getRepository($persistentObjectName, $persistentManagerName = null) { @@ -205,7 +240,7 @@ public function getRepository($persistentObjectName, $persistentManagerName = nu } /** - * @inheritdoc + * {@inheritdoc} */ public function resetManager($name = null) { diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php index a47727fa..09ffcdf0 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -36,28 +36,28 @@ interface ConnectionRegistry * * @return string The default connection name */ - function getDefaultConnectionName(); + public function getDefaultConnectionName(); /** * Gets the named connection. * * @param string $name The connection name (null for the default one) * - * @return Connection + * @return object */ - function getConnection($name = null); + public function getConnection($name = null); /** * Gets an array of all registered connections * * @return array An array of Connection instances */ - function getConnections(); + public function getConnections(); /** * Gets all connection names. * * @return array An array of connection names */ - function getConnectionNames(); + public function getConnectionNames(); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php index 8055b66d..374556ed 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -41,28 +41,39 @@ class LifecycleEventArgs extends EventArgs /** * @var object */ - private $entity; + private $object; /** * Constructor * - * @param object $entity + * @param object $object * @param ObjectManager $objectManager */ - public function __construct($entity, ObjectManager $objectManager) + public function __construct($object, ObjectManager $objectManager) { - $this->entity = $entity; + $this->object = $object; $this->objectManager = $objectManager; } /** - * Retireve associated Entity. + * Retrieve associated entity. + * @deprecated * * @return object */ public function getEntity() { - return $this->entity; + return $this->object; + } + + /** + * Retrieve associated object. + * + * @return object + */ + public function getObject() + { + return $this->object; } /** @@ -74,4 +85,4 @@ public function getObjectManager() { return $this->objectManager; } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php index 4a18d167..c014d731 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -44,7 +44,7 @@ class LoadClassMetadataEventArgs extends EventArgs /** * Constructor. * - * @param ClasseMetadata $classMetadata + * @param ClassMetadata $classMetadata * @param ObjectManager $objectManager */ public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php index 33c4d796..f1393658 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -56,4 +56,4 @@ public function getObjectManager() { return $this->objectManager; } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php index f67ab50e..18b65541 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -31,7 +31,7 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs { /** - * @var ObjectManager + * @var \Doctrine\Common\Persistence\ObjectManager */ private $objectManager; @@ -43,7 +43,7 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs /** * Constructor. * - * @param ObjectManager $objectManager + * @param \Doctrine\Common\Persistence\ObjectManager $objectManager * @param string $entityClass Optional entity class */ public function __construct($objectManager, $entityClass = null) @@ -55,7 +55,7 @@ public function __construct($objectManager, $entityClass = null) /** * Retrieve associated ObjectManager. * - * @return ObjectManager + * @return \Doctrine\Common\Persistence\ObjectManager */ public function getObjectManager() { @@ -81,4 +81,4 @@ public function clearsAllEntities() { return ($this->entityClass === null); } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php index 191d053a..86ac8193 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -64,6 +64,8 @@ public function getEntityChangeSet() /** * Check if field has a changeset. * + * @param string $field + * * @return boolean */ public function hasChangedField($field) @@ -114,6 +116,8 @@ public function setNewValue($field, $value) * Assert the field exists in changeset. * * @param string $field + * + * @throws \InvalidArgumentException */ private function assertValidField($field) { diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php index 4d92426b..b0679056 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -36,7 +36,7 @@ interface ManagerRegistry extends ConnectionRegistry * * @return string The default object manager name */ - function getDefaultManagerName(); + public function getDefaultManagerName(); /** * Gets a named object manager. @@ -45,14 +45,14 @@ function getDefaultManagerName(); * * @return \Doctrine\Common\Persistence\ObjectManager */ - function getManager($name = null); + public function getManager($name = null); /** * Gets an array of all registered object managers * - * @return array An array of ObjectManager instances + * @return \Doctrine\Common\Persistence\ObjectManager[] An array of ObjectManager instances */ - function getManagers(); + public function getManagers(); /** * Resets a named object manager. @@ -71,7 +71,7 @@ function getManagers(); * * @return \Doctrine\Common\Persistence\ObjectManager */ - function resetManager($name = null); + public function resetManager($name = null); /** * Resolves a registered namespace alias to the full namespace. @@ -82,14 +82,14 @@ function resetManager($name = null); * * @return string The full namespace */ - function getAliasNamespace($alias); + public function getAliasNamespace($alias); /** * Gets all connection names. * * @return array An array of connection names */ - function getManagerNames(); + public function getManagerNames(); /** * Gets the ObjectRepository for an persistent object. @@ -99,7 +99,7 @@ function getManagerNames(); * * @return \Doctrine\Common\Persistence\ObjectRepository */ - function getRepository($persistentObject, $persistentManagerName = null); + public function getRepository($persistentObject, $persistentManagerName = null); /** * Gets the object manager associated with a given class. @@ -108,5 +108,5 @@ function getRepository($persistentObject, $persistentManagerName = null); * * @return \Doctrine\Common\Persistence\ObjectManager|null */ - function getManagerForClass($class); + public function getManagerForClass($class); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index a2a61850..6985ff97 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -13,13 +13,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ namespace Doctrine\Common\Persistence\Mapping; -use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\Cache, + Doctrine\Common\Util\ClassUtils; /** * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the @@ -134,7 +135,7 @@ abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); /** * Return the mapping driver implementation. * - * @return MappingDriver + * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver */ abstract protected function getDriver(); @@ -151,54 +152,69 @@ abstract protected function wakeupReflection(ClassMetadata $class, ReflectionSer * Initialize Reflection after ClassMetadata was constructed. * * @param ClassMetadata $class - * @param ReflectionSErvice $reflService + * @param ReflectionService $reflService * @return void */ abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + /** + * Checks whether the class metadata is an entity. + * + * This method should false for mapped superclasses or + * embedded classes. + * + * @param ClassMetadata $class + * @return boolean + */ + abstract protected function isEntity(ClassMetadata $class); + /** * Gets the class metadata descriptor for a class. * * @param string $className The name of the class. - * @return Doctrine\Common\Persistence\Mapping\ClassMetadata + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata */ public function getMetadataFor($className) { - if ( ! isset($this->loadedMetadata[$className])) { - $realClassName = $className; + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } - // Check for namespace alias - if (strpos($className, ':') !== false) { - list($namespaceAlias, $simpleClassName) = explode(':', $className); - $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + $realClassName = $className; - if (isset($this->loadedMetadata[$realClassName])) { - // We do not have the alias name in the map, include it - $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = ClassUtils::getRealClass($realClassName); + } - return $this->loadedMetadata[$realClassName]; - } - } + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; - if ($this->cacheDriver) { - if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) { - $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 - ); - } - } + return $this->loadedMetadata[$realClassName]; + } + + if ($this->cacheDriver) { + if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) { + $this->loadedMetadata[$realClassName] = $cached; + $this->wakeupReflection($cached, $this->getReflectionService()); } else { - $this->loadMetadata($realClassName); + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + $loadedClassName . $this->cacheSalt, $this->loadedMetadata[$loadedClassName], null + ); + } } + } else { + $this->loadMetadata($realClassName); + } - if ($className != $realClassName) { - // We do not have the alias name in the map, include it - $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; - } + 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]; @@ -250,8 +266,15 @@ protected function getParentClasses($name) * 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 necesarily 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. - * @param array $tables The metadata collection to which the loaded metadata is added. + * + * @return array */ protected function loadMetadata($name) { @@ -272,7 +295,7 @@ protected function loadMetadata($name) foreach ($parentClasses as $className) { if (isset($this->loadedMetadata[$className])) { $parent = $this->loadedMetadata[$className]; - if (isset($parent->isMappedSuperclass) && $parent->isMappedSuperclass === false) { + if ($this->isEntity($parent)) { $rootEntityFound = true; array_unshift($visited, $className); } @@ -282,13 +305,13 @@ protected function loadMetadata($name) $class = $this->newClassMetadataInstance($className); $this->initializeReflection($class, $reflService); - $this->doLoadMetadata($class, $parent, $rootEntityFound); + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); $this->loadedMetadata[$className] = $class; $parent = $class; - if (isset($parent->isMappedSuperclass) && $class->isMappedSuperclass === false) { + if ($this->isEntity($class)) { $rootEntityFound = true; array_unshift($visited, $className); } @@ -305,11 +328,12 @@ protected function loadMetadata($name) * Actually load the metadata from the underlying metadata * * @param ClassMetadata $class - * @param ClassMetadata $parent + * @param ClassMetadata|null $parent * @param bool $rootEntityFound + * @param array $nonSuperclassParents classnames all parent classes that are not marked as mapped superclasses * @return void */ - abstract protected function doLoadMetadata($class, $parent, $rootEntityFound); + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); /** * Creates a new ClassMetadata instance for the given class name. @@ -331,6 +355,12 @@ public function isTransient($class) $this->initialize(); } + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + return $this->getDriver()->isTransient($class); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php index 705d59ac..640f1034 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -35,7 +35,7 @@ interface ClassMetadata * * @return string */ - function getName(); + public function getName(); /** * Gets the mapped identifier field name. @@ -44,14 +44,14 @@ function getName(); * * @return array */ - function getIdentifier(); + public function getIdentifier(); /** * Gets the ReflectionClass instance for this mapped class. * - * @return ReflectionClass + * @return \ReflectionClass */ - function getReflectionClass(); + public function getReflectionClass(); /** * Checks if the given field name is a mapped identifier for this class. @@ -59,7 +59,7 @@ function getReflectionClass(); * @param string $fieldName * @return boolean */ - function isIdentifier($fieldName); + public function isIdentifier($fieldName); /** * Checks if the given field is a mapped property for this class. @@ -67,7 +67,7 @@ function isIdentifier($fieldName); * @param string $fieldName * @return boolean */ - function hasField($fieldName); + public function hasField($fieldName); /** * Checks if the given field is a mapped association for this class. @@ -75,7 +75,7 @@ function hasField($fieldName); * @param string $fieldName * @return boolean */ - function hasAssociation($fieldName); + public function hasAssociation($fieldName); /** * Checks if the given field is a mapped single valued association for this class. @@ -83,7 +83,7 @@ function hasAssociation($fieldName); * @param string $fieldName * @return boolean */ - function isSingleValuedAssociation($fieldName); + public function isSingleValuedAssociation($fieldName); /** * Checks if the given field is a mapped collection valued association for this class. @@ -91,7 +91,7 @@ function isSingleValuedAssociation($fieldName); * @param string $fieldName * @return boolean */ - function isCollectionValuedAssociation($fieldName); + public function isCollectionValuedAssociation($fieldName); /** * A numerically indexed list of field names of this persistent class. @@ -100,14 +100,14 @@ function isCollectionValuedAssociation($fieldName); * * @return array */ - function getFieldNames(); + public function getFieldNames(); /** * Returns an array of identifier field names numerically indexed. * * @return array */ - function getIdentifierFieldNames(); + public function getIdentifierFieldNames(); /** * A numerically indexed list of association names of this persistent class. @@ -116,7 +116,7 @@ function getIdentifierFieldNames(); * * @return array */ - function getAssociationNames(); + public function getAssociationNames(); /** * Returns a type name of this field. @@ -127,7 +127,7 @@ function getAssociationNames(); * @param string $fieldName * @return string */ - function getTypeOfField($fieldName); + public function getTypeOfField($fieldName); /** * Returns the target class name of the given association. @@ -135,7 +135,7 @@ function getTypeOfField($fieldName); * @param string $assocName * @return string */ - function getAssociationTargetClass($assocName); + public function getAssociationTargetClass($assocName); /** * Checks if the association is the inverse side of a bidirectional association @@ -143,7 +143,7 @@ function getAssociationTargetClass($assocName); * @param string $assocName * @return boolean */ - function isAssociationInverseSide($assocName); + public function isAssociationInverseSide($assocName); /** * Returns the target field of the owning side of the association @@ -151,7 +151,7 @@ function isAssociationInverseSide($assocName); * @param string $assocName * @return string */ - function getAssociationMappedByTargetField($assocName); + public function getAssociationMappedByTargetField($assocName); /** * Return the identifier of this object as an array with field name as key. @@ -161,5 +161,5 @@ function getAssociationMappedByTargetField($assocName); * @param object $object * @return array */ - function getIdentifierValues($object); + public function getIdentifierValues($object); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php index bf27ba9a..cdb54e9f 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -36,7 +36,7 @@ interface ClassMetadataFactory * * @return array The ClassMetadata instances of all mapped classes. */ - function getAllMetadata(); + public function getAllMetadata(); /** * Gets the class metadata descriptor for a class. @@ -44,7 +44,7 @@ function getAllMetadata(); * @param string $className The name of the class. * @return ClassMetadata */ - function getMetadataFor($className); + public function getMetadataFor($className); /** * Checks whether the factory has the metadata for a class loaded already. @@ -52,7 +52,7 @@ function getMetadataFor($className); * @param string $className * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. */ - function hasMetadataFor($className); + public function hasMetadataFor($className); /** * Sets the metadata descriptor for a specific class. @@ -60,7 +60,7 @@ function hasMetadataFor($className); * @param string $className * @param ClassMetadata $class */ - function setMetadataFor($className, $class); + public function setMetadataFor($className, $class); /** * Whether the class with the specified name should have its metadata loaded. @@ -70,5 +70,5 @@ function setMetadataFor($className, $class); * @param string $className * @return boolean */ - function isTransient($className); + public function isTransient($className); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php index f52d37ee..b02d308a 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -118,7 +118,7 @@ public function getReader() /** * Get the file extension used to look for mapping files under * - * @return void + * @return string */ public function getFileExtension() { @@ -184,7 +184,7 @@ public function getAllClassNames() new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY ), - '/^.+' . str_replace('.', '\.', $this->fileExtension) . '$/i', + '/^.+' . preg_quote($this->fileExtension) . '$/i', \RecursiveRegexIterator::GET_MATCH ); diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php index efaf545e..0da0a698 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -24,7 +24,7 @@ /** * Locate the file that contains the metadata information for a given class name. * - * This behavior is inpependent of the actual content of the file. It just detects + * This behavior is independent of the actual content of the file. It just detects * the file which is responsible for the given class name. * * @author Benjamin Eberlei @@ -51,6 +51,7 @@ class DefaultFileLocator implements FileLocator * documents and operates in the specified operating mode. * * @param string|array $paths One or multiple paths where mapping documents can be found. + * @param string|null $fileExtension */ public function __construct($paths, $fileExtension = null) { @@ -81,7 +82,7 @@ public function getPaths() /** * Get the file extension used to look for mapping files under * - * @return void + * @return string */ public function getFileExtension() { @@ -108,7 +109,7 @@ public function findMappingFile($className) // Check whether file exists foreach ($this->paths as $path) { - if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { return $path . DIRECTORY_SEPARATOR . $fileName; } } @@ -159,11 +160,11 @@ public function fileExists($className) // Check whether file exists foreach ((array) $this->paths as $path) { - if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { return true; } } return false; } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php index 22cf117d..b0a7685c 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -58,7 +58,7 @@ abstract class FileDriver implements MappingDriver * Initializes a new FileDriver that looks in the given path(s) for mapping * documents and operates in the specified operating mode. * - * @param string|array|FileLocator $paths A FileLocator or one/multiple paths where mapping documents can be found. + * @param string|array|FileLocator $locator A FileLocator or one/multiple paths where mapping documents can be found. * @param string $fileExtension */ public function __construct($locator, $fileExtension = null) @@ -70,11 +70,21 @@ public function __construct($locator, $fileExtension = null) } } + /** + * Set global basename + * + * @param string $file + */ public function setGlobalBasename($file) { $this->globalBasename = $file; } + /** + * Retrieve global basename + * + * @return string + */ public function getGlobalBasename() { return $this->globalBasename; @@ -84,7 +94,10 @@ public function getGlobalBasename() * Get 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 * - * @return array $element The element of schema meta data + * @param string $className + * + * @throws MappingException + * @return array The element of schema meta data */ public function getElement($className) { @@ -97,6 +110,9 @@ public function getElement($className) } $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + if (!isset($result[$className])) { + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension()); + } return $result[$className]; } @@ -175,4 +191,24 @@ protected function initialize() } } } -} \ No newline at end of file + + /** + * Retrieve the locator used to discover mapping files by className + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Set the locator used to discover mapping files by className + * + * @param FileLocator $locator + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php index a1019d71..1b69a287 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -22,7 +22,7 @@ /** * Locate the file that contains the metadata information for a given class name. * - * This behavior is inpependent of the actual content of the file. It just detects + * This behavior is independent of the actual content of the file. It just detects * the file which is responsible for the given class name. * * @author Benjamin Eberlei @@ -36,7 +36,7 @@ interface FileLocator * @param string $className * @return string */ - function findMappingFile($className); + public function findMappingFile($className); /** * Get all class names that are found with this file locator. @@ -44,26 +44,28 @@ function findMappingFile($className); * @param string $globalBasename Passed to allow excluding the basename * @return array */ - function getAllClassNames($globalBasename); + public function getAllClassNames($globalBasename); /** * Check if a file can be found for this class name. * + * @param string $className + * * @return bool */ - function fileExists($className); + public function fileExists($className); /** * Get all the paths that this file locator looks for mapping files. * * @return array */ - function getPaths(); + public function getPaths(); /** * Get the file extension that mapping files are suffixed with. * * @return string */ - function getFileExtension(); + public function getFileExtension(); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php index c050d323..e893e236 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -35,14 +35,14 @@ interface MappingDriver * @param string $className * @param ClassMetadata $metadata */ - function loadMetadataForClass($className, ClassMetadata $metadata); + public function loadMetadataForClass($className, ClassMetadata $metadata); /** * Gets the names of all mapped classes known to this driver. * * @return array The names of all mapped classes known to this driver. */ - function getAllClassNames(); + public function getAllClassNames(); /** * Whether the class with the specified name should have its metadata loaded. @@ -52,5 +52,5 @@ function getAllClassNames(); * @param string $className * @return boolean */ - function isTransient($className); -} \ No newline at end of file + public function isTransient($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php index c7c14527..b7d61eee 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -35,15 +35,42 @@ */ class MappingDriverChain implements MappingDriver { + /** + * The default driver + * + * @var MappingDriver + */ + private $defaultDriver; + /** * @var array */ private $drivers = array(); + /** + * Get the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param MappingDriver $driver + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + /** * Add a nested driver. * - * @param Driver $nestedDriver + * @param MappingDriver $nestedDriver * @param string $namespace */ public function addDriver(MappingDriver $nestedDriver, $namespace) @@ -65,10 +92,13 @@ public function getDrivers() * Loads the metadata for the specified class into the provided container. * * @param string $className - * @param ClassMetadataInfo $metadata + * @param ClassMetadata $metadata + * + * @throws MappingException */ public function loadMetadataForClass($className, ClassMetadata $metadata) { + /* @var $driver MappingDriver */ foreach ($this->drivers as $namespace => $driver) { if (strpos($className, $namespace) === 0) { $driver->loadMetadataForClass($className, $metadata); @@ -76,6 +106,11 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) } } + if (null !== $this->defaultDriver) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + return; + } + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); } @@ -88,8 +123,11 @@ public function getAllClassNames() { $classNames = array(); $driverClasses = array(); + + /* @var $driver MappingDriver */ foreach ($this->drivers AS $namespace => $driver) { $oid = spl_object_hash($driver); + if (!isset($driverClasses[$oid])) { $driverClasses[$oid] = $driver->getAllClassNames(); } @@ -100,6 +138,13 @@ public function getAllClassNames() } } } + + if (null !== $this->defaultDriver) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + return array_keys($classNames); } @@ -113,13 +158,17 @@ public function getAllClassNames() */ public function isTransient($className) { + /* @var $driver MappingDriver */ foreach ($this->drivers AS $namespace => $driver) { if (strpos($className, $namespace) === 0) { return $driver->isTransient($className); } } - // class isTransient, i.e. not an entity or mapped superclass + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + return true; } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php index 7751dae3..e0c86113 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -66,5 +66,7 @@ protected function loadMappingFile($file) { $metadata = $this->metadata; include $file; + + return array($metadata->getName() => $metadata); } } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php index 9103ed86..e3cea730 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -50,11 +50,21 @@ class StaticPHPDriver implements MappingDriver */ private $classNames; + /** + * Constructor + * + * @param array|string $paths + */ public function __construct($paths) { $this->addPaths((array) $paths); } + /** + * Add paths + * + * @param array $paths + */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php index d338cf60..9095187d 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -53,18 +53,34 @@ class SymfonyFileLocator implements FileLocator */ protected $fileExtension; + /** + * Constructor + * + * @param array $prefixes + * @param string|null $fileExtension + */ public function __construct(array $prefixes, $fileExtension = null) { $this->addNamespacePrefixes($prefixes); $this->fileExtension = $fileExtension; } + /** + * Add Namespace Prefixes + * + * @param array $prefixes + */ public function addNamespacePrefixes(array $prefixes) { $this->prefixes = array_merge($this->prefixes, $prefixes); $this->paths = array_merge($this->paths, array_keys($prefixes)); } + /** + * Get Namespace Prefixes + * + * @return array + */ public function getNamespacePrefixes() { return $this->prefixes; diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php index 4ecd2ad5..9dd4014f 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -26,18 +26,32 @@ */ class MappingException extends \Exception { + /** + * + * @param string $className + * @param array $namespaces + * + * @return MappingException + */ public static function classNotFoundInNamespaces($className, $namespaces) { return new self("The class '" . $className . "' was not found in the ". "chain configured namespaces " . implode(", ", $namespaces)); } + /** + * @return MappingException + */ public static function pathRequired() { return new self("Specifying the paths to your entities is required ". "in the AnnotationDriver to retrieve all class names."); } + /** + * @param string|null $path + * @return MappingException + */ public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) { if ( ! empty($path)) { @@ -50,8 +64,33 @@ public static function fileMappingDriversRequireConfiguredDirectoryPath($path = ); } + /** + * @param string $entityName + * @param string $fileName + * @return MappingException + */ public static function mappingFileNotFound($entityName, $fileName) { return new self("No mapping file found named '$fileName' for class '$entityName'."); } + + /** + * @param string $entityName + * @param string $fileName + * @return MappingException + */ + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } + + /** + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\MappingException + */ + public static function nonExistingClass($className) + { + return new self("Class '$className' does not exists"); + } } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php index 4e0e312f..10818059 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -33,9 +33,12 @@ interface ReflectionService * Return an array of the parent classes (not interfaces) for the given class. * * @param string $class + * + * @throws \Doctrine\Common\Persistence\Mapping\MappingException + * * @return array */ - function getParentClasses($class); + public function getParentClasses($class); /** * Return the shortname of a class. @@ -43,30 +46,30 @@ function getParentClasses($class); * @param string $class * @return string */ - function getClassShortName($class); + public function getClassShortName($class); /** * @param string $class * @return string */ - function getClassNamespace($class); + public function getClassNamespace($class); /** * Return a reflection class instance or null * * @param string $class - * @return ReflectionClass|null + * @return \ReflectionClass|null */ - function getClass($class); + public function getClass($class); /** * Return an accessible property (setAccessible(true)) or null. * * @param string $class * @param string $property - * @return ReflectionProperty|null + * @return \ReflectionProperty|null */ - function getAccessibleProperty($class, $property); + public function getAccessibleProperty($class, $property); /** * Check if the class have a public method with the given name. @@ -75,6 +78,6 @@ function getAccessibleProperty($class, $property); * @param mixed $method * @return bool */ - function hasPublicMethod($class, $method); + public function hasPublicMethod($class, $method); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php index abcff581..ee51ec08 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -21,6 +21,8 @@ use ReflectionClass; use ReflectionProperty; +use Doctrine\Common\Reflection\RuntimePublicReflectionProperty; +use Doctrine\Common\Persistence\Mapping\MappingException; /** * PHP Runtime Reflection Service @@ -30,43 +32,39 @@ class RuntimeReflectionService implements ReflectionService { /** - * Return an array of the parent classes (not interfaces) for the given class. - * - * @param string $class - * @return array + * {@inheritDoc} */ public function getParentClasses($class) { + if ( ! class_exists($class)) { + throw MappingException::nonExistingClass($class); + } + return class_parents($class); } /** - * Return the shortname of a class. - * - * @param string $class - * @return string + * {@inheritDoc} */ public function getClassShortName($class) { - $r = new ReflectionClass($class); - return $r->getShortName(); + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getShortName(); } /** - * @param string $class - * @return string + * {@inheritDoc} */ public function getClassNamespace($class) { - $r = new ReflectionClass($class); - return $r->getNamespaceName(); + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); } /** - * Return a reflection class instance or null - * - * @param string $class - * @return ReflectionClass|null + * {@inheritDoc} */ public function getClass($class) { @@ -74,25 +72,23 @@ public function getClass($class) } /** - * Return an accessible property (setAccessible(true)) or null. - * - * @param string $class - * @param string $property - * @return ReflectionProperty|null + * {@inheritDoc} */ public function getAccessibleProperty($class, $property) { - $property = new ReflectionProperty($class, $property); - $property->setAccessible(true); - return $property; + $reflectionProperty = new ReflectionProperty($class, $property); + + if ($reflectionProperty->isPublic()) { + $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; } /** - * Check if the class have a public method with the given name. - * - * @param mixed $class - * @param mixed $method - * @return bool + * {@inheritDoc} */ public function hasPublicMethod($class, $method) { diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php index 2de6e761..4f6d1cfa 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php index 6d70fc12..e6f24833 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -39,7 +39,7 @@ interface ObjectManager * @param mixed * @return object */ - function find($className, $id); + public function find($className, $id); /** * Tells the ObjectManager to make an instance managed and persistent. @@ -51,7 +51,7 @@ function find($className, $id); * * @param object $object The instance to make managed and persistent. */ - function persist($object); + public function persist($object); /** * Removes an object instance. @@ -60,7 +60,7 @@ function persist($object); * * @param object $object The object instance to remove. */ - function remove($object); + public function remove($object); /** * Merges the state of a detached object into the persistence context @@ -68,8 +68,17 @@ function remove($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 $objectName if given, only objects of this type will get detached */ - function merge($object); + public function clear($objectName = null); /** * Detaches an object from the ObjectManager, causing a managed object to @@ -80,7 +89,7 @@ function merge($object); * * @param object $object The object to detach. */ - function detach($object); + public function detach($object); /** * Refreshes the persistent state of an object from the database, @@ -88,14 +97,14 @@ function detach($object); * * @param object $object The object to refresh. */ - function refresh($object); + 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. */ - function flush(); + public function flush(); /** * Gets the repository for a class. @@ -103,7 +112,7 @@ function flush(); * @param string $className * @return \Doctrine\Common\Persistence\ObjectRepository */ - function getRepository($className); + public function getRepository($className); /** * Returns the ClassMetadata descriptor for a class. @@ -114,14 +123,14 @@ function getRepository($className); * @param string $className * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata */ - function getClassMetadata($className); + public function getClassMetadata($className); /** * Gets the metadata factory used to gather the metadata of classes. * - * @return Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory */ - function getMetadataFactory(); + public function getMetadataFactory(); /** * Helper method to initialize a lazy loading proxy or persistent collection. @@ -130,7 +139,7 @@ function getMetadataFactory(); * * @param object $obj */ - function initializeObject($obj); + public function initializeObject($obj); /** * Check if the object is part of the current UnitOfWork and therefore @@ -139,5 +148,5 @@ function initializeObject($obj); * @param object $object * @return bool */ - function contains($object); + public function contains($object); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php index 015dd3dd..93bde061 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -26,7 +26,7 @@ * * Using this interface the managing object manager and class metadata instances * are injected into the persistent object after construction. This allows - * you to implement ActiveRecord functionality on top of the persistance-ignorance + * you to implement ActiveRecord functionality on top of the persistence-ignorance * that Doctrine propagates. * * Word of Warning: This is a very powerful hook to change how you can work with your domain models. diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php new file mode 100644 index 00000000..8946475d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Base class to simplify ObjectManager decorators + * + * @license http://opensource.org/licenses/MIT MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +abstract class ObjectManagerDecorator implements ObjectManager +{ + /** + * @var ObjectManager + */ + protected $wrapped; + + /** + * {@inheritdoc} + */ + public function find($className, $id) + { + return $this->wrapped->find($className, $id); + } + + /** + * {@inheritdoc} + */ + public function persist($object) + { + return $this->wrapped->persist($object); + } + + /** + * {@inheritdoc} + */ + public function remove($object) + { + return $this->wrapped->remove($object); + } + + /** + * {@inheritdoc} + */ + public function merge($object) + { + return $this->wrapped->merge($object); + } + + /** + * {@inheritdoc} + */ + public function clear($objectName = null) + { + return $this->wrapped->clear($objectName); + } + + /** + * {@inheritdoc} + */ + public function detach($object) + { + return $this->wrapped->detach($object); + } + + /** + * {@inheritdoc} + */ + public function refresh($object) + { + return $this->wrapped->refresh($object); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + return $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) + { + return $this->wrapped->initializeObject($obj); + } + + /** + * {@inheritdoc} + */ + public function contains($object) + { + return $this->wrapped->contains($object); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php index 22633288..ff3224d5 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -33,17 +33,17 @@ interface ObjectRepository /** * Finds an object by its primary key / identifier. * - * @param $id The identifier. + * @param int $id The identifier. * @return object The object. */ - function find($id); + public function find($id); /** * Finds all objects in the repository. * * @return mixed The objects. */ - function findAll(); + public function findAll(); /** * Finds objects by a set of criteria. @@ -52,14 +52,14 @@ function findAll(); * an UnexpectedValueException if certain values of the sorting or limiting details are * not supported. * - * @throws UnexpectedValueException + * @throws \UnexpectedValueException * @param array $criteria * @param array|null $orderBy * @param int|null $limit * @param int|null $offset * @return mixed The objects. */ - function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); /** * Finds a single object by a set of criteria. @@ -67,12 +67,12 @@ function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = * @param array $criteria * @return object The object. */ - function findOneBy(array $criteria); + public function findOneBy(array $criteria); /** * Returns the class name of the object managed by the repository * * @return string */ - function getClassName(); + public function getClassName(); } diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php index 4274af62..3b08c2d7 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -38,7 +38,7 @@ * This is either done on `postLoad` of an object or by accessing the global object manager. * 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). * 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. - * 5. Only the inverse side associations get autoset on the owning side aswell. Setting objects on the owning side + * 5. Only the inverse side associations get autoset on the owning side as well. Setting objects on the owning side * will not set the inverse side associations. * * @example @@ -90,6 +90,8 @@ static public function getObjectManager() * * @param ObjectManager $objectManager * @param ClassMetadata $classMetadata + * + * @throws \RuntimeException */ public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) { @@ -104,10 +106,11 @@ public function injectObjectManager(ObjectManager $objectManager, ClassMetadata /** * Sets a persistent fields value. * - * @throws InvalidArgumentException - When the wrong target object type is passed to an association - * @throws BadMethodCallException - When no persistent field exists by that name. * @param string $field * @param array $args + * + * @throws \BadMethodCallException - When no persistent field exists by that name. + * @throws \InvalidArgumentException - When the wrong target object type is passed to an association * @return void */ private function set($field, $args) @@ -131,8 +134,10 @@ private function set($field, $args) /** * Get persistent field value. * - * @throws BadMethodCallException - When no persistent field exists by that name. + * * @param string $field + * + * @throws \BadMethodCallException - When no persistent field exists by that name. * @return mixed */ private function get($field) @@ -155,7 +160,7 @@ private function get($field) */ private function completeOwningSide($field, $targetClass, $targetObject) { - // add this object on the owning side aswell, for obvious infinite recursion + // 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)) { $mappedByField = $this->cm->getAssociationMappedByTargetField($field); @@ -169,8 +174,11 @@ private function completeOwningSide($field, $targetClass, $targetObject) /** * Add an object to a collection * - * @param type $field - * @param assoc $args + * @param string $field + * @param array $args + * + * @throws \BadMethodCallException + * @throws \InvalidArgumentException */ private function add($field, $args) { @@ -194,6 +202,7 @@ private function add($field, $args) /** * Initialize Doctrine Metadata for this class. * + * @throws \RuntimeException * @return void */ private function initializeDoctrine() @@ -214,6 +223,8 @@ private function initializeDoctrine() * * @param string $method * @param array $args + * + * @throws \BadMethodCallException * @return mixed */ public function __call($method, $args) diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php index 726979fc..9c1bf5f0 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php @@ -14,7 +14,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -38,7 +38,7 @@ interface Proxy /** * Length of the proxy marker * - * @var int + * @var integer */ const MARKER_LENGTH = 6; diff --git a/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php index 87c5b413..1171874b 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -15,7 +15,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ 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 00000000..b8ba6774 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Abstract factory for proxy objects. + * + * @author Benjamin Eberlei + */ +abstract class AbstractProxyFactory +{ + /** + * @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 bool Whether to automatically (re)generate proxy classes. + */ + private $autoGenerate; + + /** + * @var \Doctrine\Common\Proxy\ProxyDefinition[] + */ + private $definitions = array(); + + /** + * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory + * @param bool $autoGenerate + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->proxyGenerator = $proxyGenerator; + $this->metadataFactory = $metadataFactory; + $this->autoGenerate = $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 + */ + 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) { + $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. + * + * @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); + + if ($this->autoGenerate) { + $this->proxyGenerator->generateProxyClass($classMetadata); + } + + require $fileName; + } + + 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); + + /** + * @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 00000000..4859a772 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + * + * @author Benjamin Eberlei + */ +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); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) + 1)); + + return $proxyDir . DIRECTORY_SEPARATOR . $className . '.php'; + } + + /** + * Register and return autoloader callback for the given proxy dir and + * namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable $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 00000000..d215d0c7 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use Doctrine\Common\Persistence\Proxy; +use InvalidArgumentException as BaseInvalidArgumentException; + +/** + * Proxy Invalid Argument Exception + * + * @link www.doctrine-project.com + * @since 2.4 + * @author Marco Pivetta + */ +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'); + } + + /** + * @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)); + } +} 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 00000000..7fa747c1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +/** + * Base exception interface for proxy exceptions + * + * @link www.doctrine-project.com + * @since 2.4 + * @author Marco Pivetta + */ +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 00000000..a466a722 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Proxy Unexpected Value Exception + * + * @link www.doctrine-project.com + * @since 2.4 + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryNotWritable() + { + return new self('Your proxy directory must be writable'); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * + * @return self + */ + public static function invalidParameterTypeHint($className, $methodName, $parameterName, \Exception $previous) + { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $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 00000000..b654951f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Proxy as BaseProxy; +use Closure; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @author Marco Pivetta + * @since 2.4 + */ +interface Proxy extends BaseProxy +{ + /** + * Marks the proxy as initialized or not. + * + * @param boolean $initialized + */ + public function __setInitialized($initialized); + + /** + * Set 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 $initializer + */ + public function __setInitializer(Closure $initializer = null); + + /** + * Retrieves the initializer callback used to initialize the proxy. + * @see __setInitializer + * + * @return Closure|null + */ + public function __getInitializer(); + + /** + * Set 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 $cloner + */ + 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 with keys being the property names, and values being 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 00000000..48b149a3 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +/** + * Definition structure how to create a proxy. + * + * @author Benjamin Eberlei + */ +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 00000000..45822452 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,896 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; + +/** + * This factory is used to generate proxy classes. It builds proxies from given parameters, a template and class + * metadata. + * + * @author Marco Pivetta + * @since 2.4 + */ +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{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i'; + + /** + * @var string The namespace that contains all proxy classes. + */ + private $proxyNamespace; + + /** + * @var string The directory that contains all proxy classes. + */ + private $proxyDirectory; + + /** + * @var string[]|callable[] map of callables used to fill in placeholders set in the template + */ + protected $placeholders = array( + 'baseProxyInterface' => 'Doctrine\Common\Proxy\Proxy', + 'additionalProperties' => '', + ); + + /** + * @var string template used as a blueprint to generate proxies + */ + 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 = array(); + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', array()); + } + + /** + * {@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; + } + + /** + * Set 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; + } + + /** + * Set 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 $fileName Filename (full path) for the generated class + * + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = null) + { + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = array(); + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = isset($this->placeholders[$name]) + ? $this->placeholders[$name] + : array($this, 'generate' . $name); + } + + foreach ($placeholders as & $placeholder) { + if (is_callable($placeholder)) { + $placeholder = call_user_func($placeholder, $class); + } + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + $fileName = $fileName ?: $this->getProxyFileName($class->getName()); + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { + throw UnexpectedValueException::proxyDirectoryNotWritable(); + } + + if ( ! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable(); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + rename($tmpFileName, $fileName); + } + + /** + * 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 = array(); + + 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 = array(); + + 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)); + $hasParentGet = $class->getReflectionClass()->hasMethod('__get'); + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $inheritDoc = $hasParentGet ? '{@inheritDoc}' : ''; + $magicGet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', array($name)); + + return $this->$name; + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', array($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', array($name, $value)); + + $this->$name = $value; + + return; + } + + +EOT; + } + + if ($hasParentSet) { + $magicSet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', array($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', array($name)); + + return isset($this->$name); + } + + +EOT; + } + + if ($hasParentIsset) { + $magicIsset .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', array($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 = array('__isInitialized__'); + + /* @var $prop \ReflectionProperty */ + foreach ($class->getReflectionClass()->getProperties() as $prop) { + $allProperties[] = $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 array($allProperties); + } + + return array($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 = array(); + $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', array()); +$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 = array(); + $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC); + $skippedMethods = array( + '__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 . '('; + + $firstParam = true; + $parameterString = ''; + $argumentString = ''; + $parameters = array(); + + foreach ($method->getParameters() as $param) { + if ($firstParam) { + $firstParam = false; + } else { + $parameterString .= ', '; + $argumentString .= ', '; + } + + try { + $paramClass = $param->getClass(); + } catch (\ReflectionException $previous) { + throw UnexpectedValueException::invalidParameterTypeHint( + $class->getName(), + $method->getName(), + $param->getName(), + $previous + ); + } + + // We need to pick the type hint class too + if (null !== $paramClass) { + $parameterString .= '\\' . $paramClass->getName() . ' '; + } elseif ($param->isArray()) { + $parameterString .= 'array '; + } elseif (method_exists($param, 'isCallable') && $param->isCallable()) { + $parameterString .= 'callable '; + } + + if ($param->isPassedByReference()) { + $parameterString .= '&'; + } + + $parameters[] = '$' . $param->getName(); + $parameterString .= '$' . $param->getName(); + $argumentString .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterString .= ' = ' . var_export($param->getDefaultValue(), true); + } + } + + $methods .= $parameterString . ')'; + $methods .= "\n" . ' {' . "\n"; + + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($name, 3)); + $fieldType = $class->getTypeOfField($identifier); + $cast = in_array($fieldType, array('integer', 'smallint')) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return ' . $cast . ' parent::' . $method->getName() . "();\n"; + $methods .= ' }' . "\n\n"; + } + + $methods .= "\n \$this->__initializer__ " + . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true) + . ", array(" . implode(', ', $parameters) . "));" + . "\n\n return parent::" . $name . '(' . $argumentString . ');' + . "\n" . ' }' . "\n"; + } + + return $methods; + } + + /** + * Generate 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'; + } + + /** + * Check 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 = array(); + + 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; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php new file mode 100644 index 00000000..ae696076 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +interface ClassFinderInterface +{ + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return + * The name of the class or NULL if not found. + */ + public function findFile($class); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php new file mode 100644 index 00000000..d29bbc55 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +class Psr0FindFile implements ClassFinderInterface +{ + /** + * The PSR-0 prefixes. + * + * @var string + */ + protected $prefixes; + + /** + * @param string $prefixes + * An array of prefixes. Each key is a PHP namespace and each value is + * a list of directories. + */ + public function __construct($prefixes) + { + $this->prefixes = $prefixes; + } + + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return + * The name of the class or NULL if not found. + */ + public function findFile($class) + { + $lastNsPos = strrpos($class, '\\'); + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $lastNsPos) { + // 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 (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php new file mode 100644 index 00000000..a436a2d7 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +interface ReflectionProviderInterface +{ + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionClass + */ + public function getReflectionClass(); + + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionMethod + */ + public function getReflectionMethod($name); + + /** + * Get the ReflectionClass equivalent for this class. + * + * @return ReflectionMethod + */ + public function getReflectionProperty($name); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php new file mode 100644 index 00000000..3211917c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionProperty; +use Doctrine\Common\Proxy\Proxy; + +/** + * PHP Runtime Reflection Public Property - special overrides for public properties + * + * @author Marco Pivetta + * @since 2.4 + */ +class RuntimePublicReflectionProperty extends ReflectionProperty +{ + /** + * {@inheritDoc} + * + * Checks is the value actually exist before fetching it. + * This is to avoid calling `__get` on the provided $object if it + * is a {@see \Doctrine\Common\Proxy\Proxy}. + */ + public function getValue($object = null) + { + $name = $this->getName(); + + if ($object instanceof Proxy && ! $object->__isInitialized()) { + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + $val = isset($object->$name) ? $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/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php new file mode 100644 index 00000000..12e45d57 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionClass; +use ReflectionException; + +class StaticReflectionClass extends ReflectionClass +{ + /** + * The static reflection parser object. + * + * @var StaticReflectionParser + */ + private $staticReflectionParser; + + public function __construct(StaticReflectionParser $staticReflectionParser) + { + $this->staticReflectionParser = $staticReflectionParser; + } + + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + public static function export($argument, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getConstant($name) { throw new ReflectionException('Method not implemented'); } + public function getConstants() { throw new ReflectionException('Method not implemented'); } + public function getConstructor() { throw new ReflectionException('Method not implemented'); } + public function getDefaultProperties() { throw new ReflectionException('Method not implemented'); } + public function getEndLine() { throw new ReflectionException('Method not implemented'); } + public function getExtension() { throw new ReflectionException('Method not implemented'); } + public function getExtensionName() { throw new ReflectionException('Method not implemented'); } + public function getFileName() { throw new ReflectionException('Method not implemented'); } + public function getInterfaceNames() { throw new ReflectionException('Method not implemented'); } + public function getInterfaces() { throw new ReflectionException('Method not implemented'); } + public function getMethods($filter = NULL) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getParentClass() { throw new ReflectionException('Method not implemented'); } + public function getProperties($filter = NULL) { throw new ReflectionException('Method not implemented'); } + public function getShortName() { throw new ReflectionException('Method not implemented'); } + public function getStartLine() { throw new ReflectionException('Method not implemented'); } + public function getStaticProperties() { throw new ReflectionException('Method not implemented'); } + public function getStaticPropertyValue($name, $default = '') { throw new ReflectionException('Method not implemented'); } + public function getTraitAliases() { throw new ReflectionException('Method not implemented'); } + public function getTraitNames() { throw new ReflectionException('Method not implemented'); } + public function getTraits() { throw new ReflectionException('Method not implemented'); } + public function hasConstant($name) { throw new ReflectionException('Method not implemented'); } + public function hasMethod($name) { throw new ReflectionException('Method not implemented'); } + public function hasProperty($name) { throw new ReflectionException('Method not implemented'); } + public function implementsInterface($interface) { throw new ReflectionException('Method not implemented'); } + public function inNamespace() { throw new ReflectionException('Method not implemented'); } + public function isAbstract() { throw new ReflectionException('Method not implemented'); } + public function isCloneable() { throw new ReflectionException('Method not implemented'); } + public function isFinal() { throw new ReflectionException('Method not implemented'); } + public function isInstance($object) { throw new ReflectionException('Method not implemented'); } + public function isInstantiable() { throw new ReflectionException('Method not implemented'); } + public function isInterface() { throw new ReflectionException('Method not implemented'); } + public function isInternal() { throw new ReflectionException('Method not implemented'); } + public function isIterateable() { throw new ReflectionException('Method not implemented'); } + public function isSubclassOf($class) { throw new ReflectionException('Method not implemented'); } + public function isTrait() { throw new ReflectionException('Method not implemented'); } + public function isUserDefined() { throw new ReflectionException('Method not implemented'); } + public function newInstance($args) { throw new ReflectionException('Method not implemented'); } + public function newInstanceArgs(array $args = array()) { throw new ReflectionException('Method not implemented'); } + public function newInstanceWithoutConstructor() { throw new ReflectionException('Method not implemented'); } + public function setStaticPropertyValue($name, $value) { throw new ReflectionException('Method not implemented'); } + public function __toString() { throw new ReflectionException('Method not implemented'); } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php new file mode 100644 index 00000000..6482036e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionMethod; +use ReflectionException; + +class StaticReflectionMethod extends ReflectionMethod +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the method. + * + * @var string + */ + protected $methodName; + + public function __construct(StaticReflectionParser $staticReflectionParser, $methodName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + public function getName() + { + return $this->methodName; + } + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + public static function export($class, $name, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getClosure($object) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getPrototype() { throw new ReflectionException('Method not implemented'); } + public function invoke($object, $parameter = NULL) { throw new ReflectionException('Method not implemented'); } + public function invokeArgs($object, array $args) { throw new ReflectionException('Method not implemented'); } + public function isAbstract() { throw new ReflectionException('Method not implemented'); } + public function isConstructor() { throw new ReflectionException('Method not implemented'); } + public function isDestructor() { throw new ReflectionException('Method not implemented'); } + public function isFinal() { throw new ReflectionException('Method not implemented'); } + public function isPrivate() { throw new ReflectionException('Method not implemented'); } + public function isProtected() { throw new ReflectionException('Method not implemented'); } + public function isPublic() { throw new ReflectionException('Method not implemented'); } + public function isStatic() { throw new ReflectionException('Method not implemented'); } + public function setAccessible($accessible) { throw new ReflectionException('Method not implemented'); } + public function __toString() { throw new ReflectionException('Method not implemented'); } + public function getClosureThis() { throw new ReflectionException('Method not implemented'); } + public function getEndLine() { throw new ReflectionException('Method not implemented'); } + public function getExtension() { throw new ReflectionException('Method not implemented'); } + public function getExtensionName() { throw new ReflectionException('Method not implemented'); } + public function getFileName() { throw new ReflectionException('Method not implemented'); } + public function getNumberOfParameters() { throw new ReflectionException('Method not implemented'); } + public function getNumberOfRequiredParameters() { throw new ReflectionException('Method not implemented'); } + public function getParameters() { throw new ReflectionException('Method not implemented'); } + public function getShortName() { throw new ReflectionException('Method not implemented'); } + public function getStartLine() { throw new ReflectionException('Method not implemented'); } + public function getStaticVariables() { throw new ReflectionException('Method not implemented'); } + public function inNamespace() { throw new ReflectionException('Method not implemented'); } + public function isClosure() { throw new ReflectionException('Method not implemented'); } + public function isDeprecated() { throw new ReflectionException('Method not implemented'); } + public function isInternal() { throw new ReflectionException('Method not implemented'); } + public function isUserDefined() { throw new ReflectionException('Method not implemented'); } + public function returnsReference() { throw new ReflectionException('Method not implemented'); } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php new file mode 100644 index 00000000..1c61b230 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,295 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use Doctrine\Common\Annotations\TokenParser; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Karoly Negyesi + */ +class StaticReflectionParser implements ReflectionProviderInterface +{ + + /** + * The fully qualified class name. + * + * @var string + */ + protected $className; + + /** + * The short class name. + * + * @var string + */ + protected $shortClassName; + + /** + * TRUE if the caller only wants class annotations. + * + * @var boolean. + */ + protected $classAnnotationOptimize; + + /** + * TRUE when the parser has ran. + * + * @var boolean + */ + protected $parsed = false; + + /** + * The namespace of the class + * + * @var string + */ + protected $namespace = ''; + + /** + * The use statements of this class. + * + * @var array + */ + protected $useStatements = array(); + + /** + * The docComment of the class. + * + * @var string + */ + protected $docComment = array( + 'class' => '', + 'property' => array(), + 'method' => array(), + ); + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Annotations\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $class + * The full, namespaced class name. + * @param ClassFinder $finder + * A ClassFinder object which finds the class. + * @param boolean $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; + } + + protected function parse() + { + if ($this->parsed || !$fileName = $this->finder->findFile($this->className)) { + return; + } + $this->parsed = true; + $contents = file_get_contents($fileName); + if ($this->classAnnotationOptimize) { + if (preg_match("/(\A.*)^\s+(abstract|final)?\s+class\s+{$this->shortClassName}\s+{/sm", $contents, $matches)) { + $contents = $matches[1]; + } + } + $tokenParser = new TokenParser($contents); + $docComment = ''; + while ($token = $tokenParser->next(false)) { + if (is_array($token)) { + 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: + $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); + $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) { + $this->parentClassName = '\\' . $use . $postfix; + $fullySpecified = true; + } + } + } + if (!$fullySpecified) { + $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName; + } + break; + } + } + } + } + + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + public function getClassName() + { + return $this->className; + } + + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * Get the ReflectionClass equivalent for this file / class. + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * Get the ReflectionMethod equivalent for the method of this file / class. + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * Get the ReflectionProperty equivalent for the method of this file / class. + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Get the use statements from this file. + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Get docComment. + * + * @param string $type class, property or method. + * @param string $name Name of the property or method, not needed for class. + * + * @return string the doc comment or empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Get the PSR-0 parser for the declaring class. + * + * @param string $type property or method. + * @param string $name Name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + */ + 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/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php new file mode 100644 index 00000000..7c6411a9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionProperty; +use ReflectionException; + +class StaticReflectionProperty extends ReflectionProperty +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the property. + * + * @var string + */ + protected $propertyName; + + public function __construct(StaticReflectionParser $staticReflectionParser, $propertyName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + public function getName() + { + return $this->propertyName; + } + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + public static function export ($class, $name, $return = false) { throw new ReflectionException('Method not implemented'); } + public function getModifiers() { throw new ReflectionException('Method not implemented'); } + public function getValue($object = NULL) { throw new ReflectionException('Method not implemented'); } + public function isDefault() { throw new ReflectionException('Method not implemented'); } + public function isPrivate() { throw new ReflectionException('Method not implemented'); } + public function isProtected() { throw new ReflectionException('Method not implemented'); } + public function isPublic() { throw new ReflectionException('Method not implemented'); } + public function isStatic() { throw new ReflectionException('Method not implemented'); } + public function setAccessible ($accessible) { throw new ReflectionException('Method not implemented'); } + public function setValue ($object, $value = NULL) { throw new ReflectionException('Method not implemented'); } + public function __toString() { throw new ReflectionException('Method not implemented'); } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php index c3462782..078a8dba 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -71,7 +71,7 @@ public static function getParentClass($className) * Create a new reflection class * * @param string - * @return ReflectionClass + * @return \ReflectionClass */ public static function newReflectionClass($class) { @@ -82,7 +82,7 @@ public static function newReflectionClass($class) * Create a new reflection object * * @param object - * @return ReflectionObject + * @return \ReflectionObject */ public static function newReflectionObject($object) { diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php index 57ae3120..a80f47e6 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -41,7 +41,6 @@ private function __construct() {} /** * Prints a dump of the public, protected and private properties of $var. * - * @static * @link http://xdebug.org/ * @param mixed $var * @param integer $maxDepth Maximum nesting level for object properties @@ -67,6 +66,13 @@ public static function dump($var, $maxDepth = 2, $stripTags = true) ini_set('html_errors', 'Off'); } + /** + * Export + * + * @param mixed $var + * @param int $maxDepth + * @return mixed + */ public static function export($var, $maxDepth) { $return = null; @@ -98,6 +104,10 @@ public static function export($var, $maxDepth) $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); } + if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) { + $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); + } + foreach ($reflClass->getProperties() as $reflProperty) { $name = $reflProperty->getName(); @@ -116,8 +126,14 @@ public static function export($var, $maxDepth) return $return; } + /** + * Convert to string + * + * @param object $obj + * @return string + */ public static function toString($obj) { - return method_exists('__toString', $obj) ? (string) $obj : get_class($obj) . '@' . spl_object_hash($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 index ba1eb17b..e1da90ea 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -1,7 +1,5 @@ . */ namespace Doctrine\Common\Util; +use Doctrine\Common\Inflector\Inflector as BaseInflector; + /** * 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 - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 1.0 - * @version $Revision: 3189 $ - * @author Konsta Vesterinen - * @author Jonathan H. Wage + * Kept for backwards compatibility reasons, was moved to its + * own component. */ -class Inflector +class Inflector extends BaseInflector { - /** - * Convert word in to the format for a Doctrine table name. Converts 'ModelName' to 'model_name' - * - * @param string $word Word to tableize - * @return string $word Tableized word - */ - public static function tableize($word) - { - return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); - } - - /** - * Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName' - * - * @param string $word Word to classify - * @return string $word Classified word - */ - public static function classify($word) - { - return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); - } - - /** - * Camelize a word. This uses the classify() method and turns the first character to lowercase - * - * @param string $word - * @return string $word - */ - public static function camelize($word) - { - return lcfirst(self::classify($word)); - } -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Version.php b/vendor/doctrine/common/lib/Doctrine/Common/Version.php index 7a87311f..3823a2c6 100644 --- a/vendor/doctrine/common/lib/Doctrine/Common/Version.php +++ b/vendor/doctrine/common/lib/Doctrine/Common/Version.php @@ -13,7 +13,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals - * and is licensed under the LGPL. For more information, see + * and is licensed under the MIT license. For more information, see * . */ @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.2.3'; + const VERSION = '2.4.0-RC3'; /** * Compares a Doctrine version with the current one. diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest.php index 567cf918..1eb2216b 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest.php @@ -42,4 +42,23 @@ public function testGetClassLoader() $this->assertNull(ClassLoader::getClassLoader('This\Class\Does\Not\Exist')); $cl->unregister(); } + + public function testClassExistsWithSilentAutoloader() + { + $test = $this; + $silentLoader = function ($className) use ($test) { + $test->assertSame('ClassLoaderTest\ClassE', $className); + require __DIR__ . '/ClassLoaderTest/ClassE.php'; + }; + $additionalLoader = function () use ($test) { + $test->fail('Should not call this loader, class was already loaded'); + }; + + $this->assertFalse(ClassLoader::classExists('ClassLoaderTest\ClassE')); + spl_autoload_register($silentLoader); + spl_autoload_register($additionalLoader); + $this->assertTrue(ClassLoader::classExists('ClassLoaderTest\ClassE')); + spl_autoload_unregister($additionalLoader); + spl_autoload_unregister($silentLoader); + } } diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest/ClassE.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest/ClassE.php new file mode 100644 index 00000000..f7a08113 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/ClassLoaderTest/ClassE.php @@ -0,0 +1,5 @@ +getMock('Doctrine\Common\Peristence\ClassMetadata'); + $classMetadata = $this->getMock('Doctrine\Common\Persistence\ClassMetadata'); $chain = new MappingDriverChain(); @@ -86,9 +86,67 @@ public function testIsTransient() $this->assertTrue($chain->isTransient('stdClass'), "stdClass isTransient"); } + + /** + * @group DDC-1412 + */ + public function testDefaultDriver() + { + $companyDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $defaultDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $entityClassName = 'Doctrine\Tests\ORM\Mapping\DriverChainEntity'; + $managerClassName = 'Doctrine\Tests\Models\Company\CompanyManager'; + $chain = new MappingDriverChain(); + + $companyDriver->expects($this->never()) + ->method('loadMetadataForClass'); + $companyDriver->expects($this->once()) + ->method('isTransient') + ->with($this->equalTo($managerClassName)) + ->will($this->returnValue(false)); + + $defaultDriver->expects($this->never()) + ->method('loadMetadataForClass'); + $defaultDriver->expects($this->once()) + ->method('isTransient') + ->with($this->equalTo($entityClassName)) + ->will($this->returnValue(true)); + + $this->assertNull($chain->getDefaultDriver()); + + $chain->setDefaultDriver($defaultDriver); + $chain->addDriver($companyDriver, 'Doctrine\Tests\Models\Company'); + + $this->assertSame($defaultDriver, $chain->getDefaultDriver()); + + $this->assertTrue($chain->isTransient($entityClassName)); + $this->assertFalse($chain->isTransient($managerClassName)); + } + + public function testDefaultDriverGetAllClassNames() + { + $companyDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $defaultDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $chain = new MappingDriverChain(); + + $companyDriver->expects($this->once()) + ->method('getAllClassNames') + ->will($this->returnValue(array('Doctrine\Tests\Models\Company\Foo'))); + + $defaultDriver->expects($this->once()) + ->method('getAllClassNames') + ->will($this->returnValue(array('Other\Class'))); + + $chain->setDefaultDriver($defaultDriver); + $chain->addDriver($companyDriver, 'Doctrine\Tests\Models\Company'); + + $classNames = $chain->getAllClassNames(); + + $this->assertEquals(array('Doctrine\Tests\Models\Company\Foo', 'Other\Class'), $classNames); + } } class DriverChainEntity { -} \ No newline at end of file +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php index 9fe32994..c5a457f6 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php @@ -39,6 +39,12 @@ public function testGetMetadataFor() $this->assertTrue($this->cmf->hasMetadataFor('stdClass')); } + public function testGetMetadataForAbsentClass() + { + $this->setExpectedException('Doctrine\Common\Persistence\Mapping\MappingException'); + $this->cmf->getMetadataFor(__NAMESPACE__ . '\AbsentClass'); + } + public function testGetParentMetadata() { $metadata = $this->cmf->getMetadataFor(__NAMESPACE__ . '\ChildEntity'); @@ -90,7 +96,7 @@ public function __construct($driver, $metadata) $this->metadata = $metadata; } - protected function doLoadMetadata($class, $parent, $rootEntityFound) + protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) { } @@ -121,6 +127,11 @@ protected function wakeupReflection(ClassMetadata $class, ReflectionService $ref protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService) { } + + protected function isEntity(ClassMetadata $class) + { + return true; + } } class RootEntity diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/DefaultFileLocatorTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/DefaultFileLocatorTest.php index 3c600c40..37072de6 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/DefaultFileLocatorTest.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/DefaultFileLocatorTest.php @@ -40,7 +40,7 @@ public function testFindMappingFile() $locator = new DefaultFileLocator(array($path), ".yml"); - $this->assertEquals(__DIR__ . '/_files/stdClass.yml', $locator->findMappingFile('stdClass')); + $this->assertEquals(__DIR__ . '/_files' . DIRECTORY_SEPARATOR . 'stdClass.yml', $locator->findMappingFile('stdClass')); } public function testFindMappingFileNotFound() diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/RuntimeReflectionServiceTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/RuntimeReflectionServiceTest.php index 5f06cad9..e372aa4b 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/RuntimeReflectionServiceTest.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/Mapping/RuntimeReflectionServiceTest.php @@ -26,8 +26,13 @@ */ class RuntimeReflectionServiceTest extends \PHPUnit_Framework_TestCase { + /** + * @var RuntimeReflectionService + */ private $reflectionService; + public $unusedPublicProperty; + public function setUp() { $this->reflectionService = new RuntimeReflectionService(); @@ -49,6 +54,12 @@ public function testGetParentClasses() $this->assertTrue(count($classes) >= 1, "The test class ".__CLASS__." should have at least one parent."); } + public function testGetParentClassesForAbsentClass() + { + $this->setExpectedException('Doctrine\Common\Persistence\Mapping\MappingException'); + $this->reflectionService->getParentClasses(__NAMESPACE__ . '\AbsentClass'); + } + public function testGetReflectionClass() { $class = $this->reflectionService->getClass(__CLASS__); @@ -65,6 +76,9 @@ public function testGetAccessibleProperty() { $reflProp = $this->reflectionService->getAccessibleProperty(__CLASS__, "reflectionService"); $this->assertInstanceOf("ReflectionProperty", $reflProp); + + $reflProp = $this->reflectionService->getAccessibleProperty(__CLASS__, "unusedPublicProperty"); + $this->assertInstanceOf("Doctrine\Common\Reflection\RuntimePublicReflectionProperty", $reflProp); } } diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/ObjectManagerDecoratorTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/ObjectManagerDecoratorTest.php new file mode 100644 index 00000000..768b4d30 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/ObjectManagerDecoratorTest.php @@ -0,0 +1,60 @@ +wrapped = $wrapped; + } +} + +class ObjectManagerDecoratorTest extends \PHPUnit_Framework_TestCase +{ + private $wrapped; + private $decorated; + + public function setUp() + { + $this->wrapped = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $this->decorated = new NullObjectManagerDecorator($this->wrapped); + } + + public function getMethodParameters() + { + $class = new \ReflectionClass('Doctrine\Common\Persistence\ObjectManager'); + + $methods = array(); + foreach ($class->getMethods() as $method) { + if ($method->getNumberOfRequiredParameters() === 0) { + $methods[] = array($method->getName(), array()); + } elseif ($method->getNumberOfRequiredParameters() > 0) { + $methods[] = array($method->getName(), array_fill(0, $method->getNumberOfRequiredParameters(), 'req') ?: array()); + } + if ($method->getNumberOfParameters() != $method->getNumberOfRequiredParameters()) { + $methods[] = array($method->getName(), array_fill(0, $method->getNumberOfParameters(), 'all') ?: array()); + } + } + + return $methods; + } + + /** + * @dataProvider getMethodParameters + */ + public function testAllMethodCallsAreDelegatedToTheWrappedInstance($method, array $parameters) + { + $stub = $this->wrapped + ->expects($this->once()) + ->method($method) + ->will($this->returnValue('INNER VALUE FROM ' . $method)); + + call_user_func_array(array($stub, 'with'), $parameters); + + $this->assertSame('INNER VALUE FROM ' . $method, call_user_func_array(array($this->decorated, $method), $parameters)); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php index a0f77b50..180d0f0d 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php @@ -84,7 +84,7 @@ public function testSetToOneAssociation() $this->assertSame($parent, $this->object->getParent($parent)); } - public function testSetInvalidToOneAssocation() + public function testSetInvalidToOneAssociation() { $parent = new \stdClass(); @@ -100,7 +100,7 @@ public function testSetToOneAssociationNull() $this->assertNull($this->object->getParent()); } - public function testAddToManyAssocation() + public function testAddToManyAssociation() { $child = new TestObject(); $this->object->addChildren($child); @@ -114,7 +114,7 @@ public function testAddToManyAssocation() $this->assertEquals(2, count($this->object->getChildren())); } - public function testAddInvalidToManyAssocation() + public function testAddInvalidToManyAssociation() { $this->setExpectedException('InvalidArgumentException'); $this->object->addChildren(new \stdClass()); diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AbstractProxyFactoryTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AbstractProxyFactoryTest.php new file mode 100644 index 00000000..8aa966d9 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AbstractProxyFactoryTest.php @@ -0,0 +1,118 @@ +getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $proxyGenerator = $this->getMock('Doctrine\Common\Proxy\ProxyGenerator', array(), array(), '', false); + + $proxyGenerator + ->expects($this->once()) + ->method('getProxyFileName'); + $proxyGenerator + ->expects($this->once()) + ->method('generateProxyClass'); + + $metadataFactory = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory'); + $proxyFactory = $this->getMockForAbstractClass( + 'Doctrine\Common\Proxy\AbstractProxyFactory', + array($proxyGenerator, $metadataFactory, true) + ); + + $proxyFactory + ->expects($this->any()) + ->method('skipClass') + ->will($this->returnValue(false)); + + $generated = $proxyFactory->generateProxyClasses(array($metadata), sys_get_temp_dir()); + + $this->assertEquals(1, $generated, 'One proxy was generated'); + } + + public function testGetProxy() + { + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $proxy = $this->getMock('Doctrine\Common\Proxy\Proxy'); + $definition = new ProxyDefinition(get_class($proxy), array(), array(), null, null); + $proxyGenerator = $this->getMock('Doctrine\Common\Proxy\ProxyGenerator', array(), array(), '', false); + $metadataFactory = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory'); + + $metadataFactory + ->expects($this->once()) + ->method('getMetadataFor') + ->will($this->returnValue($metadata)); + + $proxyFactory = $this->getMockForAbstractClass( + 'Doctrine\Common\Proxy\AbstractProxyFactory', + array($proxyGenerator, $metadataFactory, true) + ); + + $proxyFactory + ->expects($this->any()) + ->method('createProxyDefinition') + ->will($this->returnValue($definition)); + + $generatedProxy = $proxyFactory->getProxy('Class', array('id' => 1)); + + $this->assertInstanceOf(get_class($proxy), $generatedProxy); + } + + public function testResetUnitializedProxy() + { + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $proxy = $this->getMock('Doctrine\Common\Proxy\Proxy'); + $definition = new ProxyDefinition(get_class($proxy), array(), array(), null, null); + $proxyGenerator = $this->getMock('Doctrine\Common\Proxy\ProxyGenerator', array(), array(), '', false); + $metadataFactory = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory'); + + $metadataFactory + ->expects($this->once()) + ->method('getMetadataFor') + ->will($this->returnValue($metadata)); + + $proxyFactory = $this->getMockForAbstractClass( + 'Doctrine\Common\Proxy\AbstractProxyFactory', + array($proxyGenerator, $metadataFactory, true) + ); + + $proxyFactory + ->expects($this->any()) + ->method('createProxyDefinition') + ->will($this->returnValue($definition)); + + $proxy + ->expects($this->once()) + ->method('__isInitialized') + ->will($this->returnValue(false)); + $proxy + ->expects($this->once()) + ->method('__setInitializer'); + $proxy + ->expects($this->once()) + ->method('__setCloner'); + + $proxyFactory->resetUninitializedProxy($proxy); + } + + public function testDisallowsResettingInitializedProxy() + { + $proxyFactory = $this->getMockForAbstractClass('Doctrine\Common\Proxy\AbstractProxyFactory', array(), '', false); + $proxy = $this->getMock('Doctrine\Common\Proxy\Proxy'); + + $proxy + ->expects($this->any()) + ->method('__isInitialized') + ->will($this->returnValue(true)); + + $this->setExpectedException('Doctrine\Common\Proxy\Exception\InvalidArgumentException'); + + $proxyFactory->resetUninitializedProxy($proxy); + } +} + diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AutoloaderTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AutoloaderTest.php new file mode 100644 index 00000000..c15baffa --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/AutoloaderTest.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\Tests\Common\Proxy; + +use PHPUnit_Framework_TestCase; +use Doctrine\Common\Proxy\Autoloader; + +/** + * @group DDC-1698 + */ +class AutoloaderTest extends PHPUnit_Framework_TestCase +{ + public static function dataResolveFile() + { + return array( + array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'), + array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'), + array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__OtherRealClass.php'), + ); + } + + /** + * @dataProvider dataResolveFile + */ + public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile) + { + $actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + $this->assertEquals($expectedProxyFile, $actualProxyFile); + } + + public function testAutoload() + { + if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) { + unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php"); + } + + $autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) { + file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true)); + unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php"); + } + + public function testRegisterWithInvalidCallback() + { + $this->setExpectedException( + 'Doctrine\Common\Proxy\Exception\InvalidArgumentException', + 'Invalid \$notFoundCallback given: must be a callable, "stdClass" given' + ); + + Autoloader::register('', '', new \stdClass()); + } +} + diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/CallableTypeHintClass.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/CallableTypeHintClass.php new file mode 100644 index 00000000..f5fccb0d --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/CallableTypeHintClass.php @@ -0,0 +1,16 @@ +. + */ + +namespace Doctrine\Tests\Common\Proxy; + +/** + * Test asset representing a lazy loadable object + * + * @author Marco Pivetta + * @since 2.4 + */ +class LazyLoadableObject +{ + /** + * @var string + */ + public $publicIdentifierField; + + /** + * @var string + */ + protected $protectedIdentifierField; + + /** + * @var string + */ + public $publicTransientField = 'publicTransientFieldValue'; + + /** + * @var string + */ + protected $protectedTransientField = 'protectedTransientFieldValue'; + + /** + * @var string + */ + public $publicPersistentField = 'publicPersistentFieldValue'; + + /** + * @var string + */ + protected $protectedPersistentField = 'protectedPersistentFieldValue'; + + /** + * @var string + */ + public $publicAssociation = 'publicAssociationValue'; + + /** + * @var string + */ + protected $protectedAssociation = 'protectedAssociationValue'; + + /** + * @return string + */ + public function getProtectedIdentifierField() + { + return $this->protectedIdentifierField; + } + + /** + * @return string + */ + public function testInitializationTriggeringMethod() + { + return 'testInitializationTriggeringMethod'; + } + + /** + * @return string + */ + public function getProtectedAssociation() + { + return $this->protectedAssociation; + } + + /** + * @param \stdClass $param + */ + public function publicTypeHintedMethod(\stdClass $param) + { + } + + /** + * + */ + public function &byRefMethod() + { + } + + /** + * @param mixed $thisIsNotByRef + * @param &mixed $thisIsByRef + */ + public function byRefParamMethod($thisIsNotByRef, &$thisIsByRef) + { + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectClassMetadata.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectClassMetadata.php new file mode 100644 index 00000000..167386e3 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectClassMetadata.php @@ -0,0 +1,195 @@ +. + */ + +namespace Doctrine\Tests\Common\Proxy; + +use ReflectionClass; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Class metadata test asset for @see LazyLoadableObject + * + * @author Marco Pivetta + * @since 2.4 + */ +class LazyLoadableObjectClassMetadata implements ClassMetadata +{ + /** + * @var ReflectionClass + */ + protected $reflectionClass; + + /** + * @var array + */ + protected $identifier = array( + 'publicIdentifierField' => true, + 'protectedIdentifierField' => true, + ); + + /** + * @var array + */ + protected $fields = array( + 'publicIdentifierField' => true, + 'protectedIdentifierField' => true, + 'publicPersistentField' => true, + 'protectedPersistentField' => true, + ); + + /** + * @var array + */ + protected $associations = array( + 'publicAssociation' => true, + 'protectedAssociation' => true, + ); + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->getReflectionClass()->getName(); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return array_keys($this->identifier); + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + if (null === $this->reflectionClass) { + $this->reflectionClass = new \ReflectionClass(__NAMESPACE__ . '\LazyLoadableObject'); + } + + return $this->reflectionClass; + } + + /** + * {@inheritDoc} + */ + public function isIdentifier($fieldName) + { + return isset($this->identifier[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function hasField($fieldName) + { + return isset($this->fields[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function hasAssociation($fieldName) + { + return isset($this->associations[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function isSingleValuedAssociation($fieldName) + { + throw new \BadMethodCallException('not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCollectionValuedAssociation($fieldName) + { + throw new \BadMethodCallException('not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFieldNames() + { + return array_keys($this->fields); + } + + /** + * {@inheritDoc} + */ + public function getIdentifierFieldNames() + { + return $this->getIdentifier(); + } + + /** + * {@inheritDoc} + */ + public function getAssociationNames() + { + return array_keys($this->associations); + } + + /** + * {@inheritDoc} + */ + public function getTypeOfField($fieldName) + { + return 'string'; + } + + /** + * {@inheritDoc} + */ + public function getAssociationTargetClass($assocName) + { + throw new \BadMethodCallException('not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAssociationInverseSide($assocName) + { + throw new \BadMethodCallException('not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getAssociationMappedByTargetField($assocName) + { + throw new \BadMethodCallException('not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getIdentifierValues($object) + { + throw new \BadMethodCallException('not implemented'); + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicCloneClass.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicCloneClass.php new file mode 100644 index 00000000..33b983dc --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicCloneClass.php @@ -0,0 +1,37 @@ +clonedValue = 'newClonedValue'; + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicGetClass.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicGetClass.php new file mode 100644 index 00000000..0cab36a9 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicGetClass.php @@ -0,0 +1,38 @@ +testAttribute = $value; + } + + if ($name === 'publicField' || $name === 'id') { + throw new \BadMethodCallException('Should never be called for "publicField" or "id"'); + } + + $this->testAttribute = $value; + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicSleepClass.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicSleepClass.php new file mode 100644 index 00000000..3934a05e --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/MagicSleepClass.php @@ -0,0 +1,37 @@ +wakeupValue = 'newWakeupValue'; + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php new file mode 100644 index 00000000..e0c21ee6 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php @@ -0,0 +1,185 @@ +. + */ + + +namespace Doctrine\Tests\Common\Proxy; + +use Doctrine\Common\Proxy\ProxyGenerator; +use ReflectionClass; +use ReflectionMethod; +use PHPUnit_Framework_TestCase; + +/** + * Test the proxy generator. Its work is generating on-the-fly subclasses of a given model, which implement the Proxy + * pattern. + * + * @author Giorgio Sironi + * @author Marco Pivetta + */ +class ProxyClassGeneratorTest extends PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + protected $proxyClass = 'Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\LazyLoadableObject'; + + /** + * @var LazyLoadableObjectClassMetadata + */ + protected $metadata; + + /** + * @var ProxyGenerator + */ + protected $proxyGenerator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->metadata = new LazyLoadableObjectClassMetadata(); + $this->proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + + if (class_exists($this->proxyClass, false)) { + return; + } + + $this->proxyGenerator->generateProxyClass($this->metadata); + require_once $this->proxyGenerator->getProxyFileName($this->metadata->getName()); + } + + public function testReferenceProxyRespectsMethodsParametersTypeHinting() + { + $method = new ReflectionMethod($this->proxyClass, 'publicTypeHintedMethod'); + $params = $method->getParameters(); + + $this->assertEquals(1, count($params)); + $this->assertEquals('stdClass', $params[0]->getClass()->getName()); + } + + public function testProxyRespectsMethodsWhichReturnValuesByReference() + { + $method = new ReflectionMethod($this->proxyClass, 'byRefMethod'); + + $this->assertTrue($method->returnsReference()); + } + + public function testProxyRespectsByRefMethodParameters() + { + $method = new ReflectionMethod($this->proxyClass, 'byRefParamMethod'); + $parameters = $method->getParameters(); + $this->assertSame('thisIsNotByRef', $parameters[0]->getName()); + $this->assertFalse($parameters[0]->isPassedByReference()); + $this->assertSame('thisIsByRef', $parameters[1]->getName()); + $this->assertTrue($parameters[1]->isPassedByReference()); + } + + public function testCreatesAssociationProxyAsSubclassOfTheOriginalOne() + { + $this->assertTrue(is_subclass_of($this->proxyClass, $this->metadata->getName())); + } + + public function testNonNamespacedProxyGeneration() + { + $classCode = file_get_contents($this->proxyGenerator->getProxyFileName($this->metadata->getName())); + + $this->assertNotContains("class LazyLoadableObject extends \\\\" . $this->metadata->getName(), $classCode); + $this->assertContains("class LazyLoadableObject extends \\" . $this->metadata->getName(), $classCode); + } + + public function testClassWithSleepProxyGeneration() + { + if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\SleepClass', false)) { + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $reflClass = new ReflectionClass('Doctrine\Tests\Common\Proxy\SleepClass'); + $metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); + $metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array('id'))); + $metadata->expects($this->any())->method('getName')->will($this->returnValue($reflClass->getName())); + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + $proxyGenerator->generateProxyClass($metadata); + require_once $proxyGenerator->getProxyFileName($metadata->getName()); + } + + $classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxySleepClass.php'); + $this->assertEquals(1, substr_count($classCode, 'function __sleep')); + $this->assertEquals(1, substr_count($classCode, 'parent::__sleep()')); + } + + public function testClassWithCallableTypeHintOnProxiedMethod() + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('`callable` is only supported in PHP >=5.4.0'); + } + + if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\CallableTypeHintClass', false)) { + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $reflClass = new ReflectionClass('Doctrine\Tests\Common\Proxy\CallableTypeHintClass'); + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + + $metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); + $metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array('id'))); + $metadata->expects($this->any())->method('getName')->will($this->returnValue($reflClass->getName())); + + $proxyGenerator->generateProxyClass($metadata); + require_once $proxyGenerator->getProxyFileName($metadata->getName()); + } + + $classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyCallableTypeHintClass.php'); + + $this->assertEquals(1, substr_count($classCode, 'call(callable $foo)')); + } + + public function testClassWithInvalidTypeHintOnProxiedMethod() + { + $className = 'Doctrine\Tests\Common\Proxy\InvalidTypeHintClass'; + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $reflClass = new ReflectionClass($className); + $metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); + $metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array())); + $metadata->expects($this->any())->method('getName')->will($this->returnValue($className)); + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + + $this->setExpectedException( + 'Doctrine\Common\Proxy\Exception\UnexpectedValueException', + 'The type hint of parameter "foo" in method "invalidTypeHintMethod"' + .' in class "' . $className . '" is invalid.' + ); + $proxyGenerator->generateProxyClass($metadata); + } + + public function testNoConfigDirThrowsException() + { + $this->setExpectedException('Doctrine\Common\Proxy\Exception\InvalidArgumentException'); + new ProxyGenerator(null, null); + } + + public function testNoNamespaceThrowsException() + { + $this->setExpectedException('Doctrine\Common\Proxy\Exception\InvalidArgumentException'); + new ProxyGenerator(__DIR__ . '/generated', null); + } + + public function testInvalidPlaceholderThrowsException() + { + $this->setExpectedException('Doctrine\Common\Proxy\Exception\InvalidArgumentException'); + $generator = new ProxyGenerator(__DIR__ . '/generated', 'SomeNamespace'); + $generator->setPlaceholder('', array()); + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php new file mode 100644 index 00000000..9a5173c1 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php @@ -0,0 +1,696 @@ +. + */ + +namespace Doctrine\Tests\Common\Proxy; + +use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use PHPUnit_Framework_TestCase; + +/** + * Test the generated proxies behavior. These tests make assumptions about the structure of LazyLoadableObject + * + * @author Marco Pivetta + */ +class ProxyLogicTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $proxyLoader; + + /** + * @var ClassMetadata + */ + protected $lazyLoadableObjectMetadata; + + /** + * @var LazyLoadableObject|Proxy + */ + protected $lazyObject; + + protected $identifier = array( + 'publicIdentifierField' => 'publicIdentifierFieldValue', + 'protectedIdentifierField' => 'protectedIdentifierFieldValue', + ); + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Callable + */ + protected $initializerCallbackMock; + + /** + * {@inheritDoc} + */ + public function setUp() + { + $this->proxyLoader = $loader = $this->getMock('stdClass', array('load'), array(), '', false); + $this->initializerCallbackMock = $this->getMock('stdClass', array('__invoke')); + $identifier = $this->identifier; + $this->lazyLoadableObjectMetadata = $metadata = new LazyLoadableObjectClassMetadata(); + + // emulating what should happen in a proxy factory + $cloner = function (LazyLoadableObject $proxy) use ($loader, $identifier, $metadata) { + /* @var $proxy LazyLoadableObject|Proxy */ + if ($proxy->__isInitialized()) { + return; + } + + $proxy->__setInitialized(true); + $proxy->__setInitializer(null); + $original = $loader->load($identifier); + + if (null === $original) { + throw new UnexpectedValueException(); + } + + foreach ($metadata->getReflectionClass()->getProperties() as $reflProperty) { + $propertyName = $reflProperty->getName(); + + if ($metadata->hasField($propertyName) || $metadata->hasAssociation($propertyName)) { + $reflProperty->setAccessible(true); + $reflProperty->setValue($proxy, $reflProperty->getValue($original)); + } + } + }; + + $proxyClassName = 'Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\LazyLoadableObject'; + + // creating the proxy class + if (!class_exists($proxyClassName, false)) { + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + $proxyGenerator->generateProxyClass($metadata); + require_once $proxyGenerator->getProxyFileName($metadata->getName()); + } + + $this->lazyObject = new $proxyClassName($this->getClosure($this->initializerCallbackMock), $cloner); + + // setting identifiers in the proxy via reflection + foreach ($metadata->getIdentifierFieldNames() as $idField) { + $prop = $metadata->getReflectionClass()->getProperty($idField); + $prop->setAccessible(true); + $prop->setValue($this->lazyObject, $identifier[$idField]); + } + + $this->assertFalse($this->lazyObject->__isInitialized()); + } + + public function testFetchingPublicIdentifierDoesNotCauseLazyLoading() + { + $this->configureInitializerMock(0); + + $this->assertSame('publicIdentifierFieldValue', $this->lazyObject->publicIdentifierField); + } + + public function testFetchingIdentifiersViaPublicGetterDoesNotCauseLazyLoading() + { + $this->configureInitializerMock(0); + + $this->assertSame('protectedIdentifierFieldValue', $this->lazyObject->getProtectedIdentifierField()); + } + + public function testCallingMethodCausesLazyLoading() + { + $this->configureInitializerMock( + 1, + array($this->lazyObject, 'testInitializationTriggeringMethod', array()), + function (Proxy $proxy) { + $proxy->__setInitializer(null); + } + ); + + $this->lazyObject->testInitializationTriggeringMethod(); + $this->lazyObject->testInitializationTriggeringMethod(); + } + + public function testFetchingPublicFieldsCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__get', array('publicPersistentField')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', 'loadedValue'); + } + ); + + $this->assertSame('loadedValue', $this->lazyObject->publicPersistentField); + $this->assertSame('loadedValue', $this->lazyObject->publicPersistentField); + } + + public function testFetchingPublicAssociationCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__get', array('publicAssociation')), + function () use ($test) { + $test->setProxyValue('publicAssociation', 'loadedAssociation'); + } + ); + + $this->assertSame('loadedAssociation', $this->lazyObject->publicAssociation); + $this->assertSame('loadedAssociation', $this->lazyObject->publicAssociation); + } + + public function testFetchingProtectedAssociationViaPublicGetterCausesLazyLoading() + { + $this->configureInitializerMock( + 1, + array($this->lazyObject, 'getProtectedAssociation', array()), + function (Proxy $proxy) { + $proxy->__setInitializer(null); + } + ); + + $this->assertSame('protectedAssociationValue', $this->lazyObject->getProtectedAssociation()); + $this->assertSame('protectedAssociationValue', $this->lazyObject->getProtectedAssociation()); + } + + public function testLazyLoadingTriggeredOnlyAtFirstPublicPropertyRead() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__get', array('publicPersistentField')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', 'loadedValue'); + $test->setProxyValue('publicAssociation', 'publicAssociationValue'); + } + ); + + $this->assertSame('loadedValue', $this->lazyObject->publicPersistentField); + $this->assertSame('publicAssociationValue', $this->lazyObject->publicAssociation); + } + + public function testNoticeWhenReadingNonExistentPublicProperties() + { + $this->configureInitializerMock(0); + + $class = get_class($this->lazyObject); + $this->setExpectedException( + 'PHPUnit_Framework_Error_Notice', + 'Undefined property: ' . $class . '::$non_existing_property' + ); + + $this->lazyObject->non_existing_property; + } + + public function testFalseWhenCheckingNonExistentProperty() + { + $this->configureInitializerMock(0); + + $this->assertFalse(isset($this->lazyObject->non_existing_property)); + } + + public function testNoErrorWhenSettingNonExistentProperty() + { + $this->configureInitializerMock(0); + + $this->lazyObject->non_existing_property = 'now has a value'; + $this->assertSame('now has a value', $this->lazyObject->non_existing_property); + } + + public function testCloningCallsClonerWithClonedObject() + { + $lazyObject = $this->lazyObject; + $test = $this; + $cb = $this->getMock('stdClass', array('cb')); + $cb + ->expects($this->once()) + ->method('cb') + ->will($this->returnCallback(function (LazyLoadableObject $proxy) use ($lazyObject, $test) { + /* @var $proxy LazyLoadableObject|Proxy */ + $test->assertNotSame($proxy, $lazyObject); + $proxy->__setInitializer(null); + $proxy->publicAssociation = 'clonedAssociation'; + })); + + $this->lazyObject->__setCloner($this->getClosure(array($cb, 'cb'))); + + $cloned = clone $this->lazyObject; + $this->assertSame('clonedAssociation', $cloned->publicAssociation); + $this->assertNotSame($cloned, $lazyObject, 'a clone of the lazy object is retrieved'); + } + + public function testFetchingTransientPropertiesWillNotTriggerLazyLoading() + { + $this->configureInitializerMock(0); + + $this->assertSame( + 'publicTransientFieldValue', + $this->lazyObject->publicTransientField, + 'fetching public transient field won\'t trigger lazy loading' + ); + $property = $this + ->lazyLoadableObjectMetadata + ->getReflectionClass() + ->getProperty('protectedTransientField'); + $property->setAccessible(true); + $this->assertSame( + 'protectedTransientFieldValue', + $property->getValue($this->lazyObject), + 'fetching protected transient field via reflection won\'t trigger lazy loading' + ); + } + + /** + * Provided to guarantee backwards compatibility + */ + public function testLoadProxyMethod() + { + $this->configureInitializerMock(2, array($this->lazyObject, '__load', array())); + + $this->lazyObject->__load(); + $this->lazyObject->__load(); + } + + public function testLoadingWithPersisterWillBeTriggeredOnlyOnce() + { + $this + ->proxyLoader + ->expects($this->once()) + ->method('load') + ->with( + array( + 'publicIdentifierField' => 'publicIdentifierFieldValue', + 'protectedIdentifierField' => 'protectedIdentifierFieldValue', + ), + $this->lazyObject + ) + ->will($this->returnCallback(function ($id, LazyLoadableObject $lazyObject) { + // setting a value to verify that the persister can actually set something in the object + $lazyObject->publicAssociation = $id['publicIdentifierField'] . '-test'; + return true; + })); + $this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation()); + + $this->lazyObject->__load(); + $this->lazyObject->__load(); + $this->assertSame('publicIdentifierFieldValue-test', $this->lazyObject->publicAssociation); + } + + public function testFailedLoadingWillThrowException() + { + $this->proxyLoader->expects($this->any())->method('load')->will($this->returnValue(null)); + $this->setExpectedException('UnexpectedValueException'); + $this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation()); + + $this->lazyObject->__load(); + } + + public function testCloningWithPersister() + { + $this->lazyObject->publicTransientField = 'should-not-change'; + $this + ->proxyLoader + ->expects($this->exactly(2)) + ->method('load') + ->with(array( + 'publicIdentifierField' => 'publicIdentifierFieldValue', + 'protectedIdentifierField' => 'protectedIdentifierFieldValue', + )) + ->will($this->returnCallback(function () { + $blueprint = new LazyLoadableObject(); + $blueprint->publicPersistentField = 'checked-persistent-field'; + $blueprint->publicAssociation = 'checked-association-field'; + $blueprint->publicTransientField = 'checked-transient-field'; + + return $blueprint; + })); + + $firstClone = clone $this->lazyObject; + $this->assertSame( + 'checked-persistent-field', + $firstClone->publicPersistentField, + 'Persistent fields are cloned correctly' + ); + $this->assertSame( + 'checked-association-field', + $firstClone->publicAssociation, + 'Associations are cloned correctly' + ); + $this->assertSame( + 'should-not-change', + $firstClone->publicTransientField, + 'Transient fields are not overwritten' + ); + + $secondClone = clone $this->lazyObject; + $this->assertSame( + 'checked-persistent-field', + $secondClone->publicPersistentField, + 'Persistent fields are cloned correctly' + ); + $this->assertSame( + 'checked-association-field', + $secondClone->publicAssociation, + 'Associations are cloned correctly' + ); + $this->assertSame( + 'should-not-change', + $secondClone->publicTransientField, + 'Transient fields are not overwritten' + ); + + // those should not trigger lazy loading + $firstClone->__load(); + $secondClone->__load(); + } + + public function testNotInitializedProxyUnserialization() + { + $this->configureInitializerMock(); + + $serialized = serialize($this->lazyObject); + /* @var $unserialized LazyLoadableObject|Proxy */ + $unserialized = unserialize($serialized); + $reflClass = $this->lazyLoadableObjectMetadata->getReflectionClass(); + + $this->assertFalse($unserialized->__isInitialized(), 'serialization didn\'t cause intialization'); + + // Checking identifiers + $this->assertSame('publicIdentifierFieldValue', $unserialized->publicIdentifierField, 'identifiers are kept'); + $protectedIdentifierField = $reflClass->getProperty('protectedIdentifierField'); + $protectedIdentifierField->setAccessible(true); + $this->assertSame( + 'protectedIdentifierFieldValue', + $protectedIdentifierField->getValue($unserialized), + 'identifiers are kept' + ); + + // Checking transient fields + $this->assertSame( + 'publicTransientFieldValue', + $unserialized->publicTransientField, + 'transient fields are kept' + ); + $protectedTransientField = $reflClass->getProperty('protectedTransientField'); + $protectedTransientField->setAccessible(true); + $this->assertSame( + 'protectedTransientFieldValue', + $protectedTransientField->getValue($unserialized), + 'transient fields are kept' + ); + + // Checking persistent fields + $this->assertSame( + 'publicPersistentFieldValue', + $unserialized->publicPersistentField, + 'persistent fields are kept' + ); + $protectedPersistentField = $reflClass->getProperty('protectedPersistentField'); + $protectedPersistentField->setAccessible(true); + $this->assertSame( + 'protectedPersistentFieldValue', + $protectedPersistentField->getValue($unserialized), + 'persistent fields are kept' + ); + + // Checking associations + $this->assertSame('publicAssociationValue', $unserialized->publicAssociation, 'associations are kept'); + $protectedAssociationField = $reflClass->getProperty('protectedAssociation'); + $protectedAssociationField->setAccessible(true); + $this->assertSame( + 'protectedAssociationValue', + $protectedAssociationField->getValue($unserialized), + 'associations are kept' + ); + } + + public function testInitializedProxyUnserialization() + { + // persister will retrieve the lazy object itself, so that we don't have to re-define all field values + $this->proxyLoader->expects($this->once())->method('load')->will($this->returnValue($this->lazyObject)); + $this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation()); + $this->lazyObject->__load(); + + $serialized = serialize($this->lazyObject); + $reflClass = $this->lazyLoadableObjectMetadata->getReflectionClass(); + /* @var $unserialized LazyLoadableObject|Proxy */ + $unserialized = unserialize($serialized); + + $this->assertTrue($unserialized->__isInitialized(), 'serialization didn\'t cause intialization'); + + // Checking transient fields + $this->assertSame( + 'publicTransientFieldValue', + $unserialized->publicTransientField, + 'transient fields are kept' + ); + $protectedTransientField = $reflClass->getProperty('protectedTransientField'); + $protectedTransientField->setAccessible(true); + $this->assertSame( + 'protectedTransientFieldValue', + $protectedTransientField->getValue($unserialized), + 'transient fields are kept' + ); + + // Checking persistent fields + $this->assertSame( + 'publicPersistentFieldValue', + $unserialized->publicPersistentField, + 'persistent fields are kept' + ); + $protectedPersistentField = $reflClass->getProperty('protectedPersistentField'); + $protectedPersistentField->setAccessible(true); + $this->assertSame( + 'protectedPersistentFieldValue', + $protectedPersistentField->getValue($unserialized), + 'persistent fields are kept' + ); + + // Checking identifiers + $this->assertSame( + 'publicIdentifierFieldValue', + $unserialized->publicIdentifierField, + 'identifiers are kept' + ); + $protectedIdentifierField = $reflClass->getProperty('protectedIdentifierField'); + $protectedIdentifierField->setAccessible(true); + $this->assertSame( + 'protectedIdentifierFieldValue', + $protectedIdentifierField->getValue($unserialized), + 'identifiers are kept' + ); + + // Checking associations + $this->assertSame('publicAssociationValue', $unserialized->publicAssociation, 'associations are kept'); + $protectedAssociationField = $reflClass->getProperty('protectedAssociation'); + $protectedAssociationField->setAccessible(true); + $this->assertSame( + 'protectedAssociationValue', + $protectedAssociationField->getValue($unserialized), + 'associations are kept' + ); + } + + public function testInitializationRestoresDefaultPublicLazyLoadedFieldValues() + { + // setting noop persister + $this->proxyLoader->expects($this->once())->method('load')->will($this->returnValue($this->lazyObject)); + $this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation()); + + $this->assertSame( + 'publicPersistentFieldValue', + $this->lazyObject->publicPersistentField, + 'Persistent field is restored to default value' + ); + $this->assertSame( + 'publicAssociationValue', + $this->lazyObject->publicAssociation, + 'Association is restored to default value' + ); + } + + public function testSettingPublicFieldsCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__set', array('publicPersistentField', 'newPublicPersistentFieldValue')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', 'overrideValue'); + $test->setProxyValue('publicAssociation', 'newAssociationValue'); + } + ); + + $this->lazyObject->publicPersistentField = 'newPublicPersistentFieldValue'; + $this->assertSame('newPublicPersistentFieldValue', $this->lazyObject->publicPersistentField); + $this->assertSame('newAssociationValue', $this->lazyObject->publicAssociation); + } + + public function testSettingPublicAssociationCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__set', array('publicAssociation', 'newPublicAssociationValue')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', 'newPublicPersistentFieldValue'); + $test->setProxyValue('publicAssociation', 'overrideValue'); + } + ); + + $this->lazyObject->publicAssociation = 'newPublicAssociationValue'; + $this->assertSame('newPublicAssociationValue', $this->lazyObject->publicAssociation); + $this->assertSame('newPublicPersistentFieldValue', $this->lazyObject->publicPersistentField); + } + + public function testCheckingPublicFieldsCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__isset', array('publicPersistentField')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', null); + $test->setProxyValue('publicAssociation', 'setPublicAssociation'); + } + ); + + $this->assertFalse(isset($this->lazyObject->publicPersistentField)); + $this->assertNull($this->lazyObject->publicPersistentField); + $this->assertTrue(isset($this->lazyObject->publicAssociation)); + $this->assertSame('setPublicAssociation', $this->lazyObject->publicAssociation); + } + + public function testCheckingPublicAssociationCausesLazyLoading() + { + $test = $this; + $this->configureInitializerMock( + 1, + array($this->lazyObject, '__isset', array('publicAssociation')), + function () use ($test) { + $test->setProxyValue('publicPersistentField', 'newPersistentFieldValue'); + $test->setProxyValue('publicAssociation', 'setPublicAssociation'); + } + ); + + $this->assertTrue(isset($this->lazyObject->publicAssociation)); + $this->assertSame('setPublicAssociation', $this->lazyObject->publicAssociation); + $this->assertTrue(isset($this->lazyObject->publicPersistentField)); + $this->assertSame('newPersistentFieldValue', $this->lazyObject->publicPersistentField); + } + + /** + * Converts a given callable into a closure + * + * @param callable $callable + * @return \Closure + */ + public function getClosure($callable) { + return function () use ($callable) { + call_user_func_array($callable, func_get_args()); + }; + } + + /** + * Configures the current initializer callback mock with provided matcher params + * + * @param int $expectedCallCount the number of invocations to be expected. If a value< 0 is provided, `any` is used + * @param array $callParamsMatch an ordered array of parameters to be expected + * @param callable $callbackClosure a return callback closure + * + * @return \PHPUnit_Framework_MockObject_MockObject| + */ + protected function configureInitializerMock( + $expectedCallCount = 0, + array $callParamsMatch = null, + \Closure $callbackClosure = null + ) { + if (!$expectedCallCount) { + $invocationCountMatcher = $this->exactly((int) $expectedCallCount); + } else { + $invocationCountMatcher = $expectedCallCount < 0 ? $this->any() : $this->exactly($expectedCallCount); + } + + $invocationMocker = $this->initializerCallbackMock->expects($invocationCountMatcher)->method('__invoke'); + + if (null !== $callParamsMatch) { + call_user_func_array(array($invocationMocker, 'with'), $callParamsMatch); + } + + if ($callbackClosure) { + $invocationMocker->will($this->returnCallback($callbackClosure)); + } + } + + /** + * Sets a value in the current proxy object without triggering lazy loading through `__set` + * + * @link https://bugs.php.net/bug.php?id=63463 + * + * @param string $property + * @param mixed $value + */ + public function setProxyValue($property, $value) + { + $reflectionProperty = new \ReflectionProperty($this->lazyObject, $property); + $initializer = $this->lazyObject->__getInitializer(); + + // disabling initializer since setting `publicPersistentField` triggers `__set`/`__get` + $this->lazyObject->__setInitializer(null); + $reflectionProperty->setValue($this->lazyObject, $value); + $this->lazyObject->__setInitializer($initializer); + } + + /** + * Retrieves the suggested implementation of an initializer that proxy factories in O*M + * are currently following, and that should be used to initialize the current proxy object + * + * @return \Closure + */ + protected function getSuggestedInitializerImplementation() + { + $loader = $this->proxyLoader; + $identifier = $this->identifier; + + return function (LazyLoadableObject $proxy) use ($loader, $identifier) { + /* @var $proxy LazyLoadableObject|Proxy */ + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if (!isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + + if (method_exists($proxy, '__wakeup')) { + $proxy->__wakeup(); + } + + if (null === $loader->load($identifier, $proxy)) { + throw new \UnexpectedValueException('Couldn\'t load'); + } + }; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php new file mode 100644 index 00000000..4bd4955f --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php @@ -0,0 +1,285 @@ +. + */ + +namespace Doctrine\Tests\Common\Proxy; + +use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Test for behavior of proxies with inherited magic methods + * + * @author Marco Pivetta + */ +class ProxyMagicMethodsTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \Doctrine\Common\Proxy\ProxyGenerator + */ + protected $proxyGenerator; + + /** + * @var LazyLoadableObject|Proxy + */ + protected $lazyObject; + + protected $identifier = array( + 'publicIdentifierField' => 'publicIdentifierFieldValue', + 'protectedIdentifierField' => 'protectedIdentifierFieldValue', + ); + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Callable + */ + protected $initializerCallbackMock; + + /** + * {@inheritDoc} + */ + public function setUp() + { + $this->proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . '\\MagicMethodProxy'); + } + + public static function tearDownAfterClass() + { + + } + + public function testInheritedMagicGet() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicGetClass'); + $proxy = new $proxyClassName( + function (Proxy $proxy, $method, $params) use (&$counter) { + if ( ! in_array($params[0], array('publicField', 'test', 'notDefined'))) { + throw new \InvalidArgumentException('Unexpected access to field "' . $params[0] . '"'); + } + + $initializer = $proxy->__getInitializer(); + + $proxy->__setInitializer(null); + + $proxy->publicField = 'modifiedPublicField'; + $counter += 1; + + $proxy->__setInitializer($initializer); + + } + ); + + $this->assertSame('id', $proxy->id); + $this->assertSame('modifiedPublicField', $proxy->publicField); + $this->assertSame('test', $proxy->test); + $this->assertSame('not defined', $proxy->notDefined); + + $this->assertSame(3, $counter); + } + + public function testInheritedMagicSet() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicSetClass'); + $proxy = new $proxyClassName( + function (Proxy $proxy, $method, $params) use (&$counter) { + if ( ! in_array($params[0], array('publicField', 'test', 'notDefined'))) { + throw new \InvalidArgumentException('Unexpected access to field "' . $params[0] . '"'); + } + + $counter += 1; + } + ); + + $this->assertSame('id', $proxy->id); + + $proxy->publicField = 'publicFieldValue'; + + $this->assertSame('publicFieldValue', $proxy->publicField); + + $proxy->test = 'testValue'; + + $this->assertSame('testValue', $proxy->testAttribute); + + $proxy->notDefined = 'not defined'; + + $this->assertSame('not defined', $proxy->testAttribute); + $this->assertSame(3, $counter); + } + + public function testInheritedMagicSleep() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicSleepClass'); + $proxy = new $proxyClassName(); + + $this->assertSame('defaultValue', $proxy->serializedField); + $this->assertSame('defaultValue', $proxy->nonSerializedField); + + $proxy->serializedField = 'changedValue'; + $proxy->nonSerializedField = 'changedValue'; + + $unserialized = unserialize(serialize($proxy)); + + $this->assertSame('changedValue', $unserialized->serializedField); + $this->assertSame('defaultValue', $unserialized->nonSerializedField, 'Field was not returned by "__sleep"'); + } + + public function testInheritedMagicWakeup() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicWakeupClass'); + $proxy = new $proxyClassName(); + + $this->assertSame('defaultValue', $proxy->wakeupValue); + + $proxy->wakeupValue = 'changedValue'; + $unserialized = unserialize(serialize($proxy)); + + $this->assertSame('newWakeupValue', $unserialized->wakeupValue, '"__wakeup" was called'); + + $unserialized->__setInitializer(function (Proxy $proxy) { + $proxy->__setInitializer(null); + + $proxy->publicField = 'newPublicFieldValue'; + }); + + $this->assertSame('newPublicFieldValue', $unserialized->publicField, 'Proxy can still be initialized'); + } + + public function testInheritedMagicIsset() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicIssetClass'); + $proxy = new $proxyClassName(function (Proxy $proxy, $method, $params) use (&$counter) { + if (in_array($params[0], array('publicField', 'test', 'nonExisting'))) { + $initializer = $proxy->__getInitializer(); + + $proxy->__setInitializer(null); + + $proxy->publicField = 'modifiedPublicField'; + $counter += 1; + + $proxy->__setInitializer($initializer); + + return; + } + + throw new \InvalidArgumentException( + sprintf('Should not be initialized when checking isset("%s")', $params[0]) + ); + }); + + $this->assertTrue(isset($proxy->id)); + $this->assertTrue(isset($proxy->publicField)); + $this->assertTrue(isset($proxy->test)); + $this->assertFalse(isset($proxy->nonExisting)); + + $this->assertSame(3, $counter); + } + + public function testInheritedMagicClone() + { + $proxyClassName = $this->generateProxyClass(__NAMESPACE__ . '\\MagicCloneClass'); + $proxy = new $proxyClassName( + null, + function ($proxy) { + $proxy->cloned = true; + } + ); + + $cloned = clone $proxy; + + $this->assertSame('newClonedValue', $cloned->clonedValue); + $this->assertFalse($proxy->cloned); + $this->assertTrue($cloned->cloned); + } + + /** + * @param $className + * + * @return string + */ + private function generateProxyClass($className) + { + $proxyClassName = 'Doctrine\\Tests\\Common\\Proxy\\MagicMethodProxy\\__CG__\\' . $className; + + if (class_exists($proxyClassName, false)) { + return $proxyClassName; + } + + $metadata = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + + $metadata + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue($className)); + + $metadata + ->expects($this->any()) + ->method('getIdentifier') + ->will($this->returnValue(array('id'))); + + $metadata + ->expects($this->any()) + ->method('getReflectionClass') + ->will($this->returnValue(new ReflectionClass($className))); + + $metadata + ->expects($this->any()) + ->method('isIdentifier') + ->will($this->returnCallback(function ($fieldName) { + return 'id' === $fieldName; + })); + + $metadata + ->expects($this->any()) + ->method('hasField') + ->will($this->returnCallback(function ($fieldName) { + return in_array($fieldName, array('id', 'publicField')); + })); + + $metadata + ->expects($this->any()) + ->method('hasAssociation') + ->will($this->returnValue(false)); + + $metadata + ->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('id', 'publicField'))); + + $metadata + ->expects($this->any()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + + $metadata + ->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array())); + + $metadata + ->expects($this->any()) + ->method('getTypeOfField') + ->will($this->returnValue('string')); + + $this->proxyGenerator->generateProxyClass($metadata); + require_once $this->proxyGenerator->getProxyFileName($className); + + return $proxyClassName; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/SleepClass.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/SleepClass.php new file mode 100644 index 00000000..3c6ffcdb --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Proxy/SleepClass.php @@ -0,0 +1,19 @@ +getMock('stdClass', array('callGet')); + $getCheckMock->expects($this->never())->method('callGet'); + $initializer = function () use ($getCheckMock) { + call_user_func($getCheckMock); + }; + + $mockProxy = new RuntimePublicReflectionPropertyTestProxyMock(); + $mockProxy->__setInitializer($initializer); + + $reflProperty = new RuntimePublicReflectionProperty( + __NAMESPACE__ . '\RuntimePublicReflectionPropertyTestProxyMock', + 'checkedProperty' + ); + + $this->assertSame('testValue', $reflProperty->getValue($mockProxy)); + unset($mockProxy->checkedProperty); + $this->assertNull($reflProperty->getValue($mockProxy)); + } + + public function testSetValueOnProxyPublicProperty() + { + $setCheckMock = $this->getMock('stdClass', array('neverCallSet')); + $setCheckMock->expects($this->never())->method('neverCallSet'); + $initializer = function () use ($setCheckMock) { + call_user_func(array($setCheckMock, 'neverCallSet')); + }; + + $mockProxy = new RuntimePublicReflectionPropertyTestProxyMock(); + $mockProxy->__setInitializer($initializer); + + $reflProperty = new RuntimePublicReflectionProperty( + __NAMESPACE__ . '\RuntimePublicReflectionPropertyTestProxyMock', + 'checkedProperty' + ); + + $reflProperty->setValue($mockProxy, 'newValue'); + $this->assertSame('newValue', $mockProxy->checkedProperty); + + unset($mockProxy->checkedProperty); + $reflProperty->setValue($mockProxy, 'otherNewValue'); + $this->assertSame('otherNewValue', $mockProxy->checkedProperty); + + $setCheckMock = $this->getMock('stdClass', array('callSet')); + $setCheckMock->expects($this->once())->method('callSet'); + $initializer = function () use ($setCheckMock) { + call_user_func(array($setCheckMock, 'callSet')); + }; + + $mockProxy->__setInitializer($initializer); + $mockProxy->__setInitialized(true); + + unset($mockProxy->checkedProperty); + $reflProperty->setValue($mockProxy, 'againNewValue'); + $this->assertSame('againNewValue', $mockProxy->checkedProperty); + } +} + +/** + * Mock that simulates proxy public property lazy loading + */ +class RuntimePublicReflectionPropertyTestProxyMock implements Proxy +{ + /** + * @var \Closure|null + */ + private $initializer = null; + + /** + * @var \Closure|null + */ + private $initialized = false; + + /** + * @var string + */ + public $checkedProperty = 'testValue'; + + /** + * {@inheritDoc} + */ + public function __getInitializer() + { + return $this->initializer; + } + + /** + * {@inheritDoc} + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->initializer = $initializer; + } + + /** + * {@inheritDoc} + */ + public function __getLazyProperties() + { + } + + /** + * {@inheritDoc} + */ + public function __load() + { + } + + /** + * {@inheritDoc} + */ + public function __isInitialized() + { + return $this->initialized; + } + + /** + * {@inheritDoc} + */ + public function __setInitialized($initialized) + { + $this->initialized = (bool) $initialized; + } + + /** + * @param string $name + */ + public function __get($name) + { + if ($this->initializer) { + $cb = $this->initializer; + $cb(); + } + + return $this->checkedProperty; + } + + /** + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + if ($this->initializer) { + $cb = $this->initializer; + $cb(); + } + + // triggers notices if `$name` is used: see https://bugs.php.net/bug.php?id=63463 + $this->checkedProperty = $value; + } + + /** + * @param string $name + * + * @return integer + */ + public function __isset($name) + { + if ($this->initializer) { + $cb = $this->initializer; + $cb(); + } + + return isset($this->checkedProperty); + } + + /** + * {@inheritDoc} + */ + public function __setCloner(\Closure $cloner = null) + { + } + + /** + * {@inheritDoc} + */ + public function __getCloner() + { + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/SameNamespaceParent.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/SameNamespaceParent.php new file mode 100644 index 00000000..844d4a55 --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/SameNamespaceParent.php @@ -0,0 +1,7 @@ + array($testsRoot), + ); + $noParentClassName = 'Doctrine\\Tests\\Common\\Reflection\\NoParent'; + $staticReflectionParser = new StaticReflectionParser($noParentClassName, new Psr0FindFile($paths), $classAnnotationOptimize); + $declaringClassName = $staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', 'test')->getClassName(); + $this->assertEquals($noParentClassName, $declaringClassName); + + $className = 'Doctrine\\Tests\\Common\\Reflection\\FullyClassifiedParent'; + $staticReflectionParser = new StaticReflectionParser($className, new Psr0FindFile($paths), $classAnnotationOptimize); + $declaringClassName = $staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', 'test')->getClassName(); + $this->assertEquals($noParentClassName, $declaringClassName); + + $className = 'Doctrine\\Tests\\Common\\Reflection\\SameNamespaceParent'; + $staticReflectionParser = new StaticReflectionParser($className, new Psr0FindFile($paths), $classAnnotationOptimize); + $declaringClassName = $staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', 'test')->getClassName(); + $this->assertEquals($noParentClassName, $declaringClassName); + + $dummyParentClassName = 'Doctrine\\Tests\\Common\\Reflection\\Dummies\\NoParent'; + + $className = 'Doctrine\\Tests\\Common\\Reflection\\DeeperNamespaceParent'; + $staticReflectionParser = new StaticReflectionParser($className, new Psr0FindFile($paths), $classAnnotationOptimize); + $declaringClassName = $staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', 'test')->getClassName(); + $this->assertEquals($dummyParentClassName, $declaringClassName); + + $className = 'Doctrine\\Tests\\Common\\Reflection\\UseParent'; + $staticReflectionParser = new StaticReflectionParser($className, new Psr0FindFile($paths), $classAnnotationOptimize); + $declaringClassName = $staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', 'test')->getClassName(); + $this->assertEquals($dummyParentClassName, $declaringClassName); + + } + + /** + * @return array + */ + public function classAnnotationOptimize() + { + return array( + array(false), + array(true) + ); + } +} diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/UseParent.php b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/UseParent.php new file mode 100644 index 00000000..dd512d4d --- /dev/null +++ b/vendor/doctrine/common/tests/Doctrine/Tests/Common/Reflection/UseParent.php @@ -0,0 +1,9 @@ +assertEquals( "DateTime", $var->__CLASS__ ); } + + public function testExportArrayTraversable() + { + $obj = new \ArrayObject(array('foobar')); + + $var = Debug::export($obj, 2); + $this->assertContains('foobar', $var->__STORAGE__); + + $it = new \ArrayIterator(array('foobar')); + + $var = Debug::export($it, 5); + $this->assertContains('foobar', $var->__STORAGE__); + } } diff --git a/vendor/doctrine/common/tests/Doctrine/Tests/TestInit.php b/vendor/doctrine/common/tests/Doctrine/Tests/TestInit.php index e7ab51f3..07d74e67 100644 --- a/vendor/doctrine/common/tests/Doctrine/Tests/TestInit.php +++ b/vendor/doctrine/common/tests/Doctrine/Tests/TestInit.php @@ -11,14 +11,7 @@ { if (0 === strpos($class, 'Doctrine\Tests\\')) { $path = __DIR__.'/../../'.strtr($class, '\\', '/').'.php'; - if (file_exists($path) && is_readable($path)) { - require_once $path; - - return true; - } - } else if (0 === strpos($class, 'Doctrine\Common\\')) { - $path = __DIR__.'/../../../lib/'.($class = strtr($class, '\\', '/')).'.php'; - if (file_exists($path) && is_readable($path)) { + if (is_file($path) && is_readable($path)) { require_once $path; return true; @@ -26,6 +19,8 @@ } }); +require_once __DIR__ . "/../../../vendor/autoload.php"; + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace( 'Doctrine\Tests\Common\Annotations\Fixtures', __DIR__ . '/../../' ); diff --git a/vendor/doctrine/common/tests/NativePhpunitTask.php b/vendor/doctrine/common/tests/NativePhpunitTask.php index 50fd1c3d..7033283a 100644 --- a/vendor/doctrine/common/tests/NativePhpunitTask.php +++ b/vendor/doctrine/common/tests/NativePhpunitTask.php @@ -16,7 +16,7 @@ /** * A more flexible and powerful PHPUnit Task than the native Phing one. * - * Plus forward compability for PHPUnit 3.5 and later is ensured by using the PHPUnit Test Runner instead of implementing one. + * Plus forward compatibility for PHPUnit 3.5 and later is ensured by using the PHPUnit Test Runner instead of implementing one. * * @author Benjamin Eberlei */ @@ -124,7 +124,7 @@ public function main() "failures (".$result->skippedCount()." skipped, ".$result->notImplementedCount()." not implemented)"); // Hudson for example doesn't like the backslash in class names - if (file_exists($this->coverageClover)) { + if (is_file($this->coverageClover)) { $this->log("Generated Clover Coverage XML to: ".$this->coverageClover); $content = file_get_contents($this->coverageClover); $content = str_replace("\\", ".", $content); diff --git a/vendor/doctrine/dbal/LICENSE b/vendor/doctrine/dbal/LICENSE new file mode 100644 index 00000000..4a91f0bf --- /dev/null +++ b/vendor/doctrine/dbal/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 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/dbal/README.md b/vendor/doctrine/dbal/README.md new file mode 100644 index 00000000..970db7bb --- /dev/null +++ b/vendor/doctrine/dbal/README.md @@ -0,0 +1,14 @@ +# Doctrine DBAL + +Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction. + +* Master: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=master)](http://travis-ci.org/doctrine/dbal) +* 2.3: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.3)](http://travis-ci.org/doctrine/dbal) +* 2.2: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.2)](http://travis-ci.org/doctrine/dbal) + +## More resources: + +* [Website](http://www.doctrine-project.org/projects/dbal.html) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DBAL) +* [Downloads](http://github.com/doctrine/dbal/downloads) diff --git a/vendor/doctrine/dbal/UPGRADE b/vendor/doctrine/dbal/UPGRADE new file mode 100644 index 00000000..aba427f1 --- /dev/null +++ b/vendor/doctrine/dbal/UPGRADE @@ -0,0 +1,148 @@ +# Upgrade to 2.3 + +## Oracle Session Init now sets Numeric Character + +Before 2.3 the Oracle Session Init did not care about the numeric character of the Session. +This could lead to problems on non english locale systems that required a comma as a floating +point seperator in Oracle. Since 2.3, using the Oracle Session Init on connection start the +client session will be altered to set the numeric character to ".,": + + ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,' + +See [DBAL-345](http://www.doctrine-project.org/jira/browse/DBAL-345) for more details. + +## Doctrine\DBAL\Connection and Doctrine\DBAL\Statement + +The query related methods including but not limited to executeQuery, exec, query, and executeUpdate +now wrap the driver exceptions such as PDOException with DBALException to add more debugging +information such as the executed SQL statement, and any bound parameters. + +If you want to retrieve the driver specific exception, you can retrieve it by calling the +``getPrevious()`` method on DBALException. + +Before: + + catch(\PDOException $ex) { + // ... + } + +After: + + catch(\Doctrine\DBAL\DBALException $ex) { + $pdoException = $ex->getPrevious(); + // ... + } + +## Doctrine\DBAL\Connection#setCharsetSQL() removed + +This method only worked on MySQL and it is considered unsafe on MySQL to use SET NAMES UTF-8 instead +of setting the charset directly on connection already. Replace this behavior with the +connection charset option: + +Before: + + $conn = DriverManager::getConnection(array(..)); + $conn->setCharset('UTF8'); + +After: + + $conn = DriverManager::getConnection(array('charset' => 'UTF8', ..)); + +## Doctrine\DBAL\Schema\Table#renameColumn() removed + +Doctrine\DBAL\Schema\Table#renameColumn() was removed, because it drops and recreates +the column instead. There is no fix available, because a schema diff +cannot reliably detect if a column was renamed or one column was created +and another one dropped. + +You should use explicit SQL ALTER TABLE statements to change columns names. + +## Schema Filter paths + +The Filter Schema assets expression is not wrapped in () anymore for the regexp automatically. + +Before: + + $config->setFilterSchemaAssetsExpression('foo'); + +After: + + $config->setFilterSchemaAssetsExpression('(foo)'); + +## Creating MySQL Tables now defaults to UTF-8 + +If you are creating a new MySQL Table through the Doctrine API, charset/collate are +now set to 'utf8'/'utf8_unicode_ci' by default. Previously the MySQL server defaults were used. + +# Upgrade to 2.2 + +## Doctrine\DBAL\Connection#insert and Doctrine\DBAL\Connection#update + +Both methods now accept an optional last parameter $types with binding types of the values passed. +This can potentially break child classes that have overwritten one of these methods. + +## Doctrine\DBAL\Connection#executeQuery + +Doctrine\DBAL\Connection#executeQuery() got a new last parameter "QueryCacheProfile $qcp" + +## Doctrine\DBAL\Driver\Statement split + +The Driver statement was split into a ResultStatement and the normal statement extending from it. +This separates the configuration and the retrieval API from a statement. + +## MsSql Platform/SchemaManager renamed + +The MsSqlPlatform was renamed to SQLServerPlatform, the MsSqlSchemaManager was renamed +to SQLServerSchemaManager. + +## Cleanup SQLServer Platform version mess + +DBAL 2.1 and before were actually only compatible to SQL Server 2008, not earlier versions. +Still other parts of the platform did use old features instead of newly introduced datatypes +in SQL Server 2005. Starting with DBAL 2.2 you can pick the Doctrine abstraction exactly +matching your SQL Server version. + +The PDO SqlSrv driver now uses the new `SQLServer2008Platform` as default platform. +This platform uses new features of SQL Server as of version 2008. This also includes a switch +in the used fields for "text" and "blob" field types to: + + "text" => "VARCHAR(MAX)" + "blob" => "VARBINARY(MAX)" + +Additionally `SQLServerPlatform` in DBAL 2.1 and before used "DATE", "TIME" and "DATETIME2" for dates. +This types are only available since version 2008 and the introduction of an explicit +SQLServer 2008 platform makes this dependency explicit. + +An `SQLServer2005Platform` was also introduced to differentiate the features between +versions 2003, earlier and 2005. + +With this change the `SQLServerPlatform` now throws an exception for using limit queries +with an offset, since SQLServer 2003 and lower do not support this feature. + +To use the old SQL Server Platform, because you are using SQL Server 2003 and below use +the following configuration code: + + use Doctrine\DBAL\DriverManager; + use Doctrine\DBAL\Platforms\SQLServerPlatform; + use Doctrine\DBAL\Platforms\SQLServer2005Platform; + + // You are using SQL Server 2003 or earlier + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServerPlatform() + // .. additional parameters + )); + + // You are using SQL Server 2005 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServer2005Platform() + // .. additional parameters + )); + + // You are using SQL Server 2008 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + // 2008 is default platform + // .. additional parameters + )); diff --git a/vendor/doctrine/dbal/bin/doctrine-dbal b/vendor/doctrine/dbal/bin/doctrine-dbal new file mode 100644 index 00000000..7ca0142c --- /dev/null +++ b/vendor/doctrine/dbal/bin/doctrine-dbal @@ -0,0 +1,4 @@ +#!/usr/bin/env php +register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); +$classLoader->register(); + +$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; + +$helperSet = null; +if (file_exists($configFile)) { + if ( ! is_readable($configFile)) { + trigger_error( + 'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR + ); + } + + require $configFile; + + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); + +$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\DBAL\Version::VERSION); +$cli->setCatchExceptions(true); +$cli->setHelperSet($helperSet); +$cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand(), + +)); +$cli->run(); diff --git a/vendor/doctrine/dbal/bin/doctrine.php b/vendor/doctrine/dbal/bin/doctrine.php new file mode 100644 index 00000000..cd3184d0 --- /dev/null +++ b/vendor/doctrine/dbal/bin/doctrine.php @@ -0,0 +1,42 @@ +register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); +$classLoader->register(); + +$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; + +$helperSet = null; +if (file_exists($configFile)) { + if ( ! is_readable($configFile)) { + trigger_error( + 'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR + ); + } + + require $configFile; + + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); + +$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\DBAL\Version::VERSION); +$cli->setCatchExceptions(true); +$cli->setHelperSet($helperSet); +$cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + +)); +$cli->run(); diff --git a/vendor/doctrine/dbal/composer.json b/vendor/doctrine/dbal/composer.json new file mode 100644 index 00000000..c1e0c639 --- /dev/null +++ b/vendor/doctrine/dbal/composer.json @@ -0,0 +1,36 @@ +{ + "name": "doctrine/dbal", + "type": "library","version":"2.4.0-RC1", + "description": "Database Abstraction Layer", + "keywords": ["dbal", "database", "persistence", "queryobject"], + "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"} + ], + "require": { + "php": ">=5.3.2", + "doctrine/common": "2.4.*@beta" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "suggest": { + "symfony/console": "Allows use of the command line interface" + }, + "autoload": { + "psr-0": { "Doctrine\\DBAL\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php new file mode 100644 index 00000000..8ad167bc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + private $data; + private $columnCount = 0; + private $num = 0; + private $defaultFetchMode = PDO::FETCH_BOTH; + + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + public function closeCursor() + { + unset ($this->data); + } + + public function columnCount() + { + return $this->columnCount; + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 !== null || $arg3 !== null) { + throw new \InvalidArgumentException("Caching layer does not support 2nd/3rd argument to setFetchMode()"); + } + + $this->defaultFetchMode = $fetchMode; + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + public function fetch($fetchMode = null) + { + if (isset($this->data[$this->num])) { + $row = $this->data[$this->num++]; + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if ($fetchMode === PDO::FETCH_ASSOC) { + return $row; + } else if ($fetchMode === PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchMode === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } else if ($fetchMode === PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for fetching result."); + } + } + return false; + } + + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + return $rows; + } + + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + return $row[$columnIndex]; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php new file mode 100644 index 00000000..dd274779 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + static public function noResultDriverConfigured() + { + return new self("Trying to cache a query but no result driver is configured."); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php new file mode 100644 index 00000000..54c34b9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\Common\Cache\Cache; + +/** + * Query Cache Profile handles the data relevant for query caching. + * + * It is a value object, setter methods return NEW instances. + * + * @author Benjamin Eberlei + */ +class QueryCacheProfile +{ + /** + * @var Cache + */ + private $resultCacheDriver; + /** + * @var int + */ + private $lifetime = 0; + /** + * @var string + */ + private $cacheKey; + + /** + * @param int $lifetime + * @param string $cacheKey + * @param Cache $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return Cache + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return int + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + return $this->cacheKey; + } + + /** + * Generate the real cache key from query, params and types. + * + * @param string $query + * @param array $params + * @param array $types + * @return array + */ + public function generateCacheKeys($query, $params, $types) + { + $realCacheKey = $query . "-" . serialize($params) . "-" . serialize($types); + // should the key be automatically generated using the inputs or is the cache key set? + if ($this->cacheKey === null) { + $cacheKey = sha1($realCacheKey); + } else { + $cacheKey = $this->cacheKey; + } + return array($cacheKey, $realCacheKey); + } + + /** + * @param Cache $cache + * @return QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * @return QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param int $lifetime + * @return QueryCacheProfile + */ + public function setLifetime($lifetime) + { + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php new file mode 100644 index 00000000..2ce9d6a6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,239 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Connection; +use Doctrine\Common\Cache\Cache; +use PDO; + +/** + * Cache statement for SQL results. + * + * A result is saved in multiple cache keys, there is the originally specified + * cache key which is just pointing to result rows by key. The following things + * have to be ensured: + * + * 1. lifetime of the original key has to be longer than that of all the individual rows keys + * 2. if any one row key is missing the query has to be re-executed. + * + * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. + * This means that the memory usage for cached results might increase by using this feature. + */ +class ResultCacheStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $resultCache; + + /** + * + * @var string + */ + private $cacheKey; + + /** + * @var string + */ + private $realKey; + + /** + * @var int + */ + private $lifetime; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var bool + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param Statement $stmt + * @param Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param int $lifetime + */ + public function __construct(Statement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime) + { + $this->statement = $stmt; + $this->resultCache = $resultCache; + $this->cacheKey = $cacheKey; + $this->realKey = $realKey; + $this->lifetime = $lifetime; + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function closeCursor() + { + $this->statement->closeCursor(); + if ($this->emptied && $this->data !== null) { + $data = $this->resultCache->fetch($this->cacheKey); + if ( ! $data) { + $data = array(); + } + $data[$this->realKey] = $this->data; + + $this->resultCache->save($this->cacheKey, $data, $this->lifetime); + unset($this->data); + } + } + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return mixed + */ + public function fetch($fetchMode = null) + { + if ($this->data === null) { + $this->data = array(); + } + + $row = $this->statement->fetch(PDO::FETCH_ASSOC); + if ($row) { + $this->data[] = $row; + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($fetchMode == PDO::FETCH_ASSOC) { + return $row; + } else if ($fetchMode == PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchMode == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } else if ($fetchMode == PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + return false; + } + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return array + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + return $rows; + } + + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + return $row[$columnIndex]; + } + + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + public function rowCount() + { + return $this->statement->rowCount(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php new file mode 100644 index 00000000..53f7b5e7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\Common\Cache\Cache; + +/** + * Configuration container for the Doctrine DBAL. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal When adding a new configuration option just write a getter/setter + * pair and add the option to the _attributes array with a proper default value. + */ +class Configuration +{ + /** + * The attributes that are contained in the configuration. + * Values are default values. + * + * @var array + */ + protected $_attributes = array(); + + /** + * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. + * + * @param SQLLogger $logger + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return SQLLogger + */ + public function getSQLLogger() + { + return isset($this->_attributes['sqlLogger']) ? + $this->_attributes['sqlLogger'] : null; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getResultCacheImpl() + { + return isset($this->_attributes['resultCacheImpl']) ? + $this->_attributes['resultCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * Filter schema assets expression. + * + * Only include tables/sequences matching the filter expression regexp in + * schema instances generated for the active connection when calling + * {AbstractSchemaManager#createSchema()}. + * + * @param string $filterExpression + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Return filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + return null; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php new file mode 100644 index 00000000..96fce034 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -0,0 +1,1345 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, Closure, Exception, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Driver\Connection as DriverConnection, + Doctrine\Common\EventManager, + Doctrine\DBAL\DBALException, + Doctrine\DBAL\Cache\ResultCacheStatement, + Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\DBAL\Cache\ArrayStatement, + Doctrine\DBAL\Cache\CacheException; + +/** + * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like + * events, transaction isolation levels, configuration, emulated transaction nesting, + * lazy connecting and more. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + * @author Lukas Smith (MDB2 library) + * @author Benjamin Eberlei + */ +class Connection implements DriverConnection +{ + /** + * Constant for transaction isolation level READ UNCOMMITTED. + */ + const TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * Constant for transaction isolation level READ COMMITTED. + */ + const TRANSACTION_READ_COMMITTED = 2; + + /** + * Constant for transaction isolation level REPEATABLE READ. + */ + const TRANSACTION_REPEATABLE_READ = 3; + + /** + * Constant for transaction isolation level SERIALIZABLE. + */ + const TRANSACTION_SERIALIZABLE = 4; + + /** + * Represents an array of ints to be expanded by Doctrine SQL parsing. + * + * @var int + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var int + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var int + */ + const ARRAY_PARAM_OFFSET = 100; + + /** + * The wrapped driver connection. + * + * @var \Doctrine\DBAL\Driver\Connection + */ + protected $_conn; + + /** + * @var \Doctrine\DBAL\Configuration + */ + protected $_config; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + protected $_expr; + + /** + * Whether or not a connection has been established. + * + * @var boolean + */ + private $_isConnected = false; + + /** + * The transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transactions should use savepoints + * + * @var integer + */ + private $_nestTransactionsWithSavepoints; + + /** + * The parameters used during creation of the Connection instance. + * + * @var array + */ + private $_params = array(); + + /** + * The DatabasePlatform object that provides information about the + * database platform used by the connection. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The schema manager. + * + * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + protected $_schemaManager; + + /** + * The used DBAL driver. + * + * @var \Doctrine\DBAL\Driver + */ + protected $_driver; + + /** + * Flag that indicates whether the current transaction is marked for rollback only. + * + * @var boolean + */ + private $_isRollbackOnly = false; + + private $_defaultFetchMode = PDO::FETCH_ASSOC; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, + EventManager $eventManager = null) + { + $this->_driver = $driver; + $this->_params = $params; + + if (isset($params['pdo'])) { + $this->_conn = $params['pdo']; + $this->_isConnected = true; + } + + // Create default config and event manager if none given + if ( ! $config) { + $config = new Configuration(); + } + + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $this->_config = $config; + $this->_eventManager = $eventManager; + + $this->_expr = new Query\Expression\ExpressionBuilder($this); + + if ( ! isset($params['platform'])) { + $this->_platform = $driver->getDatabasePlatform(); + } else if ($params['platform'] instanceof Platforms\AbstractPlatform) { + $this->_platform = $params['platform']; + } else { + throw DBALException::invalidPlatformSpecified(); + } + + $this->_platform->setEventManager($eventManager); + + $this->_transactionIsolationLevel = $this->_platform->getDefaultTransactionIsolationLevel(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array $params + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string $database + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string + */ + public function getHost() + { + return isset($this->_params['host']) ? $this->_params['host'] : null; + } + + /** + * Gets the port of the currently connected database. + * + * @return mixed + */ + public function getPort() + { + return isset($this->_params['port']) ? $this->_params['port'] : null; + } + + /** + * Gets the username used by this connection. + * + * @return string + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string + */ + public function getPassword() + { + return isset($this->_params['password']) ? $this->_params['password'] : null; + } + + /** + * Gets the DBAL driver instance. + * + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_driver; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return \Doctrine\DBAL\Configuration + */ + public function getConfiguration() + { + return $this->_config; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the DatabasePlatform for the connection. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function getExpressionBuilder() + { + return $this->_expr; + } + + /** + * Establishes the connection with the database. + * + * @return boolean TRUE if the connection was successfully established, FALSE if + * the connection is already open. + */ + public function connect() + { + if ($this->_isConnected) return false; + + $driverOptions = isset($this->_params['driverOptions']) ? + $this->_params['driverOptions'] : array(); + $user = isset($this->_params['user']) ? $this->_params['user'] : null; + $password = isset($this->_params['password']) ? + $this->_params['password'] : null; + + $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions); + $this->_isConnected = true; + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * setFetchMode + * + * @param integer $fetchMode + */ + public function setFetchMode($fetchMode) + { + $this->_defaultFetchMode = $fetchMode; + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $statement The SQL query. + * @param array $params The query parameters. + * @return array + */ + public function fetchAssoc($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->fetch(PDO::FETCH_ASSOC); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchArray($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->fetch(PDO::FETCH_NUM); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @param int $colnum 0-indexed column number to retrieve + * @return mixed + */ + public function fetchColumn($statement, array $params = array(), $colnum = 0) + { + return $this->executeQuery($statement, $params)->fetchColumn($colnum); + } + + /** + * Whether an actual connection to the database is established. + * + * @return boolean + */ + public function isConnected() + { + return $this->_isConnected; + } + + /** + * Checks whether a transaction is currently active. + * + * @return boolean TRUE if a transaction is currently active, FALSE otherwise. + */ + public function isTransactionActive() + { + return $this->_transactionNestingLevel > 0; + } + + /** + * Executes an SQL DELETE statement on a table. + * + * @param string $tableName The name of the table on which to delete. + * @param array $identifier The deletion criteria. An associative array containing column-value pairs. + * @param array $types The types of identifiers + * @return integer The number of affected rows. + */ + public function delete($tableName, array $identifier, array $types = array()) + { + $this->connect(); + + $criteria = array(); + + foreach (array_keys($identifier) as $columnName) { + $criteria[] = $columnName . ' = ?'; + } + + if ( ! is_int(key($types))) { + $types = $this->extractTypeValues($identifier, $types); + } + + $query = 'DELETE FROM ' . $tableName . ' WHERE ' . implode(' AND ', $criteria); + + return $this->executeUpdate($query, array_values($identifier), $types); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + unset($this->_conn); + + $this->_isConnected = false; + } + + /** + * Sets the transaction isolation level. + * + * @param integer $level The level to set. + * @return integer + */ + public function setTransactionIsolation($level) + { + $this->_transactionIsolationLevel = $level; + + return $this->executeUpdate($this->_platform->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * @param string $tableName The name of the table to update. + * @param array $data An associative array containing column-value pairs. + * @param array $identifier The update criteria. An associative array containing column-value pairs. + * @param array $types Types of the merged $data and $identifier arrays in that order. + * @return integer The number of affected rows. + */ + public function update($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect(); + $set = array(); + + foreach ($data as $columnName => $value) { + $set[] = $columnName . ' = ?'; + } + + if ( ! is_int(key($types))) { + $types = $this->extractTypeValues(array_merge($data, $identifier), $types); + } + + $params = array_merge(array_values($data), array_values($identifier)); + + $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; + + return $this->executeUpdate($sql, $params, $types); + } + + /** + * Inserts a table row with specified data. + * + * @param string $tableName The name of the table to insert data into. + * @param array $data An associative array containing column-value pairs. + * @param array $types Types of the inserted data. + * @return integer The number of affected rows. + */ + public function insert($tableName, array $data, array $types = array()) + { + $this->connect(); + + if ( ! is_int(key($types))) { + $types = $this->extractTypeValues($data, $types); + } + + $query = 'INSERT INTO ' . $tableName + . ' (' . implode(', ', array_keys($data)) . ')' + . ' VALUES (' . implode(', ', array_fill(0, count($data), '?')) . ')'; + + return $this->executeUpdate($query, array_values($data), $types); + } + + /** + * Extract ordered type list from two associate key lists of data and types. + * + * @param array $data + * @param array $types + * + * @return array + */ + private function extractTypeValues(array $data, array $types) + { + $typeValues = array(); + + foreach ($data as $k => $_) { + $typeValues[] = isset($types[$k]) + ? $types[$k] + : \PDO::PARAM_STR; + } + + return $typeValues; + } + + /** + * Quote a string so it can be safely used as a table or column name, even if + * it is a reserved name. + * + * Delimiting style depends on the underlying database platform that is being used. + * + * NOTE: Just because you CAN use quoted identifiers does not mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The name to be quoted. + * @return string The quoted name. + */ + public function quoteIdentifier($str) + { + return $this->_platform->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type Type of the parameter. + * @return string The quoted parameter. + */ + public function quote($input, $type = null) + { + $this->connect(); + + list($value, $bindingType) = $this->getBindingInfo($input, $type); + return $this->_conn->quote($value, $bindingType); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array. + * + * @param string $sql The SQL query. + * @param array $params The query parameters. + * @param array $types Query parameter types. + * @return array + */ + public function fetchAll($sql, array $params = array(), $types = array()) + { + return $this->executeQuery($sql, $params, $types)->fetchAll(); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + $this->connect(); + + try { + $stmt = new Statement($statement, $this); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $statement); + } + + $stmt->setFetchMode($this->_defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param QueryCacheProfile $qcp + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + if ($qcp !== null) { + return $this->executeCacheQuery($query, $params, $types, $qcp); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + } else { + $stmt = $this->_conn->query($query); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $query, $this->resolveParams($params, $types)); + } + + $stmt->setFetchMode($this->_defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $stmt; + } + + /** + * Execute a caching query and + * + * @param string $query + * @param array $params + * @param array $types + * @param QueryCacheProfile $qcp + * @return \Doctrine\DBAL\Driver\ResultStatement + */ + public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) + { + $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl(); + if ( ! $resultCache) { + throw CacheException::noResultDriverConfigured(); + } + + list($cacheKey, $realKey) = $qcp->generateCacheKeys($query, $params, $types); + + // fetch the row pointers entry + if ($data = $resultCache->fetch($cacheKey)) { + // is the real key part of this row pointers map or is the cache only pointing to other cache keys? + if (isset($data[$realKey])) { + $stmt = new ArrayStatement($data[$realKey]); + } else if (array_key_exists($realKey, $data)) { + $stmt = new ArrayStatement(array()); + } + } + + if (!isset($stmt)) { + $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime()); + } + + $stmt->setFetchMode($this->_defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query and returns the result, + * applying a given projection/transformation function on each row of the result. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters, if any. + * @param Closure $mapper The transformation function that is applied on each row. + * The function receives a single parameter, an array, that + * represents a row of the result set. + * @return mixed The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params ?: array()); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @param string $statement + * @param integer $fetchType + * @return \Doctrine\DBAL\Driver\Statement + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + try { + switch (func_num_args()) { + case 1: + $statement = $this->_conn->query($args[0]); + break; + case 2: + $statement = $this->_conn->query($args[0], $args[1]); + break; + default: + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + break; + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $args[0]); + } + + $statement->setFetchMode($this->_defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters + * and returns the number of affected rows. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $query The SQL query. + * @param array $params The query parameters. + * @param array $types The parameter types. + * @return integer The number of affected rows. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + $result = $stmt->rowCount(); + } else { + $result = $this->_conn->exec($query); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $query, $this->resolveParams($params, $types)); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Execute an SQL statement and return the number of affected rows. + * + * @param string $statement + * @return integer The number of affected rows. + */ + public function exec($statement) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($statement); + } + + try { + $result = $this->_conn->exec($statement); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $statement); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Returns the current transaction nesting level. + * + * @return integer The nesting level. A value of 0 means there's no active transaction. + */ + public function getTransactionNestingLevel() + { + return $this->_transactionNestingLevel; + } + + /** + * Fetch the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + return $this->_conn->errorCode(); + } + + /** + * Fetch extended error information associated with the last database operation. + * + * @return array The last error information. + */ + public function errorInfo() + { + $this->connect(); + return $this->_conn->errorInfo(); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string $seqName Name of the sequence object from which the ID should be returned. + * @return string A string representation of the last inserted ID. + */ + public function lastInsertId($seqName = null) + { + $this->connect(); + return $this->_conn->lastInsertId($seqName); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this Connection instance as an (optional) parameter. + * + * If an exception occurs during execution of the function or transaction commit, + * the transaction is rolled back and the exception re-thrown. + * + * @param Closure $func The function to execute transactionally. + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $func($this); + $this->commit(); + } catch (Exception $e) { + $this->rollback(); + throw $e; + } + } + + /** + * Set if nested transactions should use savepoints + * + * @param boolean $nestTransactionsWithSavepoints + * @return void + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = $nestTransactionsWithSavepoints; + } + + /** + * Get if nested transactions should use savepoints + * + * @return boolean + */ + public function getNestTransactionsWithSavepoints() + { + return $this->_nestTransactionsWithSavepoints; + } + + /** + * Returns the savepoint name to use for nested transactions are false if they are not supported + * "savepointFormat" parameter is not set + * + * @return mixed a string with the savepoint name or false + */ + protected function _getNestedTransactionSavePointName() + { + return 'DOCTRINE2_SAVEPOINT_'.$this->_transactionNestingLevel; + } + + /** + * Starts a transaction by suspending auto-commit mode. + * + * @return void + */ + public function beginTransaction() + { + $this->connect(); + + ++$this->_transactionNestingLevel; + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"START TRANSACTION"'); + } + $this->_conn->beginTransaction(); + if ($logger) { + $logger->stopQuery(); + } + } else if ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"SAVEPOINT"'); + } + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + } + + /** + * Commits the current transaction. + * + * @return void + * @throws ConnectionException If the commit failed due to no active transaction or + * because the transaction was marked for rollback only. + */ + public function commit() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + if ($this->_isRollbackOnly) { + throw ConnectionException::commitFailedRollbackOnly(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"COMMIT"'); + } + $this->_conn->commit(); + if ($logger) { + $logger->stopQuery(); + } + } else if ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + + --$this->_transactionNestingLevel; + } + + /** + * Cancel any database changes done during the current transaction. + * + * this method can be listened with onPreTransactionRollback and onTransactionRollback + * eventlistener methods + * + * @throws ConnectionException If the rollback operation failed. + */ + public function rollBack() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"ROLLBACK"'); + } + $this->_transactionNestingLevel = 0; + $this->_conn->rollback(); + $this->_isRollbackOnly = false; + if ($logger) { + $logger->stopQuery(); + } + } else if ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); + } + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->_transactionNestingLevel; + if ($logger) { + $logger->stopQuery(); + } + } else { + $this->_isRollbackOnly = true; + --$this->_transactionNestingLevel; + } + } + + /** + * createSavepoint + * creates a new savepoint + * + * @param string $savepoint name of a savepoint to set + * @return void + */ + public function createSavepoint($savepoint) + { + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->_platform->createSavePoint($savepoint)); + } + + /** + * releaseSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to release + * @return void + */ + public function releaseSavepoint($savepoint) + { + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->_platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->_platform->releaseSavePoint($savepoint)); + } + } + + /** + * rollbackSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to rollback to + * @return void + */ + public function rollbackSavepoint($savepoint) + { + if ( ! $this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->_platform->rollbackSavePoint($savepoint)); + } + + /** + * Gets the wrapped driver connection. + * + * @return \Doctrine\DBAL\Driver\Connection + */ + public function getWrappedConnection() + { + $this->connect(); + + return $this->_conn; + } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + if ( ! $this->_schemaManager) { + $this->_schemaManager = $this->_driver->getSchemaManager($this); + } + + return $this->_schemaManager; + } + + /** + * Marks the current transaction so that the only possible + * outcome for the transaction to be rolled back. + * + * @throws ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Check whether the current transaction is marked for rollback only. + * + * @return boolean + * @throws ConnectionException If no transaction is active. + */ + public function isRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + return $this->_isRollbackOnly; + } + + /** + * Converts a given value to its database representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * @return mixed The converted value. + */ + public function convertToDatabaseValue($value, $type) + { + return Type::getType($type)->convertToDatabaseValue($value, $this->_platform); + } + + /** + * Converts a given value to its PHP representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * @return mixed The converted type. + */ + public function convertToPHPValue($value, $type) + { + return Type::getType($type)->convertToPHPValue($value, $this->_platform); + } + + /** + * Binds a set of parameters, some or all of which are typed with a PDO binding type + * or DBAL mapping type, to a given statement. + * + * @param string $stmt The statement to bind the values to. + * @param array $params The map/list of named/positional parameters. + * @param array $types The parameter types (PDO binding types or DBAL mapping types). + * @internal Duck-typing used on the $stmt parameter to support driver statements as well as + * raw PDOStatement instances. + */ + private function _bindTypedValues($stmt, array $params, array $types) + { + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($bindIndex, $value, $bindingType); + } else { + $stmt->bindValue($bindIndex, $value); + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($name, $value, $bindingType); + } else { + $stmt->bindValue($name, $value); + } + } + } + } + + /** + * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type. + * + * @param mixed $value The value to bind + * @param mixed $type The type to bind (PDO or DBAL) + * @return array [0] => the (escaped) value, [1] => the binding type + */ + private function getBindingInfo($value, $type) + { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->_platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + return array($value, $bindingType); + } + + /** + * Resolves the parameters to a format which can be displayed. + * + * @internal This is a purely internal method. If you rely on this method, you are advised to + * copy/paste the code as this method may change, or be removed without prior notice. + * + * @param array $params + * @param array $types + * + * @return array + */ + public function resolveParams(array $params, array $types) + { + $resolvedParams = array(); + + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$bindIndex] = $value; + } else { + $resolvedParams[$bindIndex] = $value; + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$name] = $value; + } else { + $resolvedParams[$name] = $value; + } + } + } + + return $resolvedParams; + } + + /** + * Create a new instance of a SQL query builder. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php new file mode 100644 index 00000000..4b41ef24 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 4628 $ + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Driver, + Doctrine\DBAL\Configuration, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\ConnectionEventArgs, + Doctrine\DBAL\Events; + +/** + * Master-Slave Connection + * + * Connection can be used with master-slave setups. + * + * Important for the understanding of this connection should be how and when + * it picks the slave or master. + * + * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection' + * or 'executeQuery' is used. + * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', + * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or + * 'prepare' is called. + * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. + * 4. One slave connection is randomly picked ONCE during a request. + * + * ATTENTION: You can write to the slave with this connection if you execute a write query without + * opening up a transaction. For example: + * + * $conn = DriverManager::getConnection(...); + * $conn->executeQuery("DELETE FROM table"); + * + * Be aware that Connection#executeQuery is a method specifically for READ + * operations only. + * + * This connection is limited to slave operations using the + * Connection#executeQuery operation only, because it wouldn't be compatible + * with the ORM or SchemaManager code otherwise. Both use all the other + * operations in a context where writes could happen to a slave, which makes + * this restricted approach necessary. + * + * You can manually connect to the master at any time by calling: + * + * $conn->connect('master'); + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', + * 'driver' => 'pdo_mysql', + * 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'slaves' => array( + * array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ) + * )); + * + * You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information. + * + * @author Lars Strojny + * @author Benjamin Eberlei + */ +class MasterSlaveConnection extends Connection +{ + /** + * Master and slave connection (one of the randomly picked slaves) + * + * @var \Doctrine\DBAL\Driver\Connection[] + */ + protected $connections = array('master' => null, 'slave' => null); + + /** + * You can keep the slave connection and then switch back to it + * during the request if you know what you are doing. + * + * @var bool + */ + protected $keepSlave = false; + + /** + * Create Master Slave Connection + * + * @param array $params + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['slaves']) || !isset($params['master']) ) { + throw new \InvalidArgumentException('master or slaves configuration missing'); + } + if ( count($params['slaves']) == 0 ) { + throw new \InvalidArgumentException('You have to configure at least one slaves.'); + } + + $params['master']['driver'] = $params['driver']; + foreach ($params['slaves'] as $slaveKey => $slave) { + $params['slaves'][$slaveKey]['driver'] = $params['driver']; + } + + $this->keepSlave = isset($params['keepSlave']) ? (bool)$params['keepSlave'] : false; + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Check if the connection is currently towards the master or not. + * + * @return bool + */ + public function isConnectedToMaster() + { + return $this->_conn !== null && $this->_conn === $this->connections['master']; + } + + /** + * {@inheritDoc} + */ + public function connect($connectionName = null) + { + $requestedConnectionChange = ($connectionName !== null); + $connectionName = $connectionName ?: 'slave'; + + if ( $connectionName !== 'slave' && $connectionName !== 'master' ) { + throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed."); + } + + // If we have a connection open, and this is not an explicit connection + // change request, then abort right here, because we are already done. + // This prevents writes to the slave in case of "keepSlave" option enabled. + if ($this->_conn && !$requestedConnectionChange) { + return false; + } + + $forceMasterAsSlave = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'master'; + $forceMasterAsSlave = true; + } + + if ($this->connections[$connectionName]) { + if ($forceMasterAsSlave) { + $this->connections['slave'] = $this->_conn = $this->connections['master']; + } else { + $this->_conn = $this->connections[$connectionName]; + } + return false; + } + + if ($connectionName === 'master') { + // Set slave connection to master to avoid invalid reads + if ($this->connections['slave'] && ! $this->keepSlave) { + unset($this->connections['slave']); + } + + $this->connections['master'] = $this->_conn = $this->connectTo($connectionName); + + if ( ! $this->keepSlave) { + $this->connections['slave'] = $this->connections['master']; + } + } else { + $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connect to a specific connection + * + * @param string $connectionName + * @return Driver + */ + protected function connectTo($connectionName) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + protected function chooseConnectionConfiguration($connectionName, $params) + { + if ($connectionName === 'master') { + return $params['master']; + } + + return $params['slaves'][array_rand($params['slaves'])]; + } + + /** + * {@inheritDoc} + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect('master'); + return parent::executeUpdate($query, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->connect('master'); + return parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + return parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->connect('master'); + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier, array $types = array()) + { + $this->connect('master'); + return parent::delete($tableName, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function update($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect('master'); + return parent::update($tableName, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function insert($tableName, array $data, array $types = array()) + { + $this->connect('master'); + return parent::insert($tableName, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $this->connect('master'); + return parent::exec($statement); + } + + /** + * {@inheritDoc} + */ + public function createSavepoint($savepoint) + { + $this->connect('master'); + + return parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + return parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + return parent::rollbackSavepoint($savepoint); + } + + public function query() + { + $this->connect('master'); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + public function prepare($statement) + { + $this->connect('master'); + + return parent::prepare($statement); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php new file mode 100644 index 00000000..440f64a4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php @@ -0,0 +1,128 @@ +getMessage(); + + return new self($msg, 0, $driverEx); + } + + /** + * Returns a human-readable representation of an array of parameters. + * This properly handles binary data by returning a hex representation. + * + * @param array $params + * + * @return string + */ + private static function formatParameters(array $params) + { + return '[' . implode(', ', array_map(function($param) { + $json = @json_encode($param); + + if (! is_string($json) || $json == 'null' && is_string($param)) { + // JSON encoding failed, this is not a UTF-8 string. + return '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"'; + } + + return $json; + }, $params)) . ']'; + } + + public static function invalidWrapperClass($wrapperClass) + { + return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ". + "subtype of \Doctrine\DBAL\Connection."); + } + + public static function invalidDriverClass($driverClass) + { + return new self("The given 'driverClass' ".$driverClass." has to implement the ". + "\Doctrine\DBAL\Driver interface."); + } + + /** + * @param string $tableName + * @return DBALException + */ + public static function invalidTableName($tableName) + { + return new self("Invalid table name specified: ".$tableName); + } + + /** + * @param string $tableName + * @return DBALException + */ + public static function noColumnsSpecifiedForTable($tableName) + { + return new self("No columns specified for table ".$tableName); + } + + public static function limitOffsetInvalid() + { + return new self("Invalid Offset in Limit Query, it has to be larger or equal to 0."); + } + + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + public static function unknownColumnType($name) + { + return new self('Unknown column type "'.$name.'" requested. Any Doctrine type that you use has ' . + 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . + 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . + 'introspection then you might have forgot to register all database types for a Doctrine Type. Use ' . + 'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' . + 'Type#getMappedDatabaseTypes(). If the type name is empty you might ' . + 'have a problem with the cache or forgot some mapping information.' + ); + } + + public static function typeNotFound($name) + { + return new self('Type to be overwritten '.$name.' does not exist.'); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php new file mode 100644 index 00000000..83649902 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.0 + */ +interface Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * @return \Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()); + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform(); + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param \Doctrine\DBAL\Connection $conn + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager(Connection $conn); + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName(); + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(Connection $conn); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php new file mode 100644 index 00000000..a618487b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Connection interface. + * Driver connections must implement this interface. + * + * This resembles (a subset of) the PDO interface. + * + * @since 2.0 + */ +interface Connection +{ + function prepare($prepareString); + function query(); + function quote($input, $type=\PDO::PARAM_STR); + function exec($statement); + function lastInsertId($name = null); + function beginTransaction(); + function commit(); + function rollBack(); + function errorCode(); + function errorInfo(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php new file mode 100644 index 00000000..2b46c992 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * @author Kim Hemsø Rasmussen + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection +{ + /** + * {@inheritdoc} + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (\PDO::PARAM_BOOL === $type) { + if ($value) { + return 'true'; + } else { + return 'false'; + } + } + return parent::quote($value, $type); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php new file mode 100644 index 00000000..8030bbc4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * Drizzle driver using PDO MySql. + * + * @author Kim Hemsø Rasmussen + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new Connection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the Drizzle MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DrizzlePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\DrizzleSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle_pdo_mysql'; + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php new file mode 100644 index 00000000..c1c2212f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,115 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Connection implements \Doctrine\DBAL\Driver\Connection +{ + private $_conn = null; + + public function __construct(array $params, $username, $password, $driverOptions = array()) + { + $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); + + if ($isPersistant) { + $this->_conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); + } else { + $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); + } + if ( ! $this->_conn) { + throw new DB2Exception(db2_conn_errormsg()); + } + } + + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if ( ! $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return new DB2Statement($stmt); + } + + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + public function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT ) { + return $input; + } else { + return "'".$input."'"; + } + } + + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + public function errorInfo() + { + return array( + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php new file mode 100644 index 00000000..82c71e5d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,111 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver, + Doctrine\DBAL\Connection; + +/** + * IBM DB2 Driver + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver implements Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * @return \Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if ( ! isset($params['protocol'])) { + $params['protocol'] = 'TCPIP'; + } + + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { + // if the host isn't localhost, use extended connection params + $params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $params['dbname'] . + ';HOSTNAME=' . $params['host'] . + ';PROTOCOL=' . $params['protocol'] . + ';UID=' . $username . + ';PWD=' . $password .';'; + if (isset($params['port'])) { + $params['dbname'] .= 'PORT=' . $params['port']; + } + + $username = null; + $password = null; + } + + return new DB2Connection($params, $username, $password, $driverOptions); + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param \Doctrine\DBAL\Connection $conn + * @return \Doctrine\DBAL\Schema\DB2SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'ibm_db2'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php new file mode 100644 index 00000000..3d076582 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,27 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Exception extends \Exception +{ + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php new file mode 100644 index 00000000..9a43f9fa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,214 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use \Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + private $_stmt = null; + + private $_bindParam = array(); + + private $_defaultFetchMode = \PDO::FETCH_BOTH; + + /** + * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG + * @var array + */ + static private $_typeMap = array( + \PDO::PARAM_INT => DB2_LONG, + \PDO::PARAM_STR => DB2_CHAR, + ); + + public function __construct($stmt) + { + $this->_stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $this->_bindParam[$column] =& $variable; + + if ($type && isset(self::$_typeMap[$type])) { + $type = self::$_typeMap[$type]; + } else { + $type = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return true; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if ( ! $this->_stmt) { + return false; + } + + $this->_bindParam = array(); + db2_free_result($this->_stmt); + $ret = db2_free_stmt($this->_stmt); + $this->_stmt = false; + return $ret; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + if ( ! $this->_stmt) { + return false; + } + return db2_num_fields($this->_stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_stmt_error(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return array( + 0 => db2_stmt_errormsg(), + 1 => db2_stmt_error(), + ); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ( ! $this->_stmt) { + return false; + } + + /*$retval = true; + if ($params !== null) { + $retval = @db2_execute($this->_stmt, $params); + } else { + $retval = @db2_execute($this->_stmt); + }*/ + if ($params === null) { + ksort($this->_bindParam); + $params = array_values($this->_bindParam); + } + $retval = @db2_execute($this->_stmt, $params); + + if ($retval === false) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return $retval; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + switch ($fetchMode) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + default: + throw new DB2Exception("Given Fetch-Style " . $fetchMode . " is not supported."); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + if ($row && isset($row[$columnIndex])) { + return $row[$columnIndex]; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php new file mode 100644 index 00000000..60defba7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver as DriverInterface; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver implements DriverInterface +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new MysqliConnection($params, $username, $password, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php new file mode 100644 index 00000000..7ffa2ca8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliConnection implements Connection +{ + /** + * @var \mysqli + */ + private $_conn; + + public function __construct(array $params, $username, $password, array $driverOptions = array()) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + + $this->_conn = mysqli_init(); + if ( ! $this->_conn->real_connect($params['host'], $username, $password, $params['dbname'], $port, $socket)) { + throw new MysqliException($this->_conn->connect_error, $this->_conn->connect_errno); + } + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieve mysqli native resource handle. + * + * Could be used if part of your application is not using DBAL + * + * @return \mysqli + */ + public function getWrappedResourceHandle() + { + return $this->_conn; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new MysqliStatement($this->_conn, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + return "'". $this->_conn->escape_string($input) ."'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $this->_conn->query($statement); + return $this->_conn->affected_rows; + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return $this->_conn->insert_id; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->_conn->query('START TRANSACTION'); + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->_conn->commit(); + } + + /** + * {@inheritdoc}non-PHPdoc) + */ + public function rollBack() + { + return $this->_conn->rollback(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_conn->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_conn->error; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php new file mode 100644 index 00000000..139ce8fa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,26 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\Mysqli; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliException extends \Exception +{} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php new file mode 100644 index 00000000..b9f4d67c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + protected static $_paramTypeMap = array( + PDO::PARAM_STR => 's', + PDO::PARAM_BOOL => 'i', + PDO::PARAM_NULL => 's', + PDO::PARAM_INT => 'i', + PDO::PARAM_LOB => 's' // TODO Support LOB bigger then max package size. + ); + + protected $_conn; + protected $_stmt; + + /** + * @var null|false|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * Contains ref values for bindValue() + * + * @var array + */ + protected $_values = array(); + + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + public function __construct(\mysqli $conn, $prepareString) + { + $this->_conn = $conn; + $this->_stmt = $conn->prepare($prepareString); + if (false === $this->_stmt) { + throw new MysqliException($this->_conn->error, $this->_conn->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + // Index 0 is types + // Need to init the string else php think we are trying to access it as a array. + $bindedValues = array(0 => str_repeat('s', $paramCount)); + $null = null; + for ($i = 1; $i < $paramCount; $i++) { + $bindedValues[] =& $null; + } + $this->_bindedValues = $bindedValues; + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->_bindedValues[0][$column - 1] = $type; + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_values[$param] = $value; + $this->_bindedValues[$param] =& $this->_values[$param]; + $this->_bindedValues[0][$param - 1] = $type; + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if (null !== $this->_bindedValues) { + if (null !== $params) { + if ( ! $this->_bindValues($params)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + if (!call_user_func_array(array($this->_stmt, 'bind_param'), $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } + } + + if ( ! $this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + $columnNames = array(); + foreach ($meta->fetch_fields() as $col) { + $columnNames[] = $col->name; + } + $meta->free(); + + $this->_columnNames = $columnNames; + $this->_rowBindedValues = array_fill(0, count($columnNames), NULL); + + $refs = array(); + foreach ($this->_rowBindedValues as $key => &$value) { + $refs[$key] =& $value; + } + + if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + $this->_columnNames = false; + } + } + + // We have a result. + if (false !== $this->_columnNames) { + $this->_stmt->store_result(); + } + return true; + } + + /** + * Bind a array of values to bound parameters + * + * @param array $values + * @return boolean + */ + private function _bindValues($values) + { + $params = array(); + $types = str_repeat('s', count($values)); + $params[0] = $types; + + foreach ($values as &$v) { + $params[] =& $v; + } + return call_user_func_array(array($this->_stmt, 'bind_param'), $params); + } + + /** + * @return boolean|array + */ + private function _fetch() + { + $ret = $this->_stmt->fetch(); + + if (true === $ret) { + $values = array(); + foreach ($this->_rowBindedValues as $v) { + // Mysqli converts them to a scalar type it can fit in. + $values[] = null === $v ? null : (string)$v; + } + return $values; + } + return $ret; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $values = $this->_fetch(); + if (null === $values) { + return null; + } + + if (false === $values) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_NUM: + return $values; + + case PDO::FETCH_ASSOC: + return array_combine($this->_columnNames, $values); + + case PDO::FETCH_BOTH: + $ret = array_combine($this->_columnNames, $values); + $ret += $values; + return $ret; + + default: + throw new MysqliException("Unknown fetch type '{$fetchMode}'"); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + $rows = array(); + if (PDO::FETCH_COLUMN == $fetchMode) { + while (($row = $this->fetchColumn()) !== false) { + $rows[] = $row; + } + } else { + while (($row = $this->fetch($fetchMode)) !== null) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (null === $row) { + return false; + } + return $row[$columnIndex]; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_stmt->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_stmt->error; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->_stmt->free_result(); + return true; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + if (false === $this->_columnNames) { + return $this->_stmt->affected_rows; + } + return $this->_stmt->num_rows; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->_stmt->field_count; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php new file mode 100644 index 00000000..d512610e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Platforms; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new OCI8Connection( + $username, + $password, + $this->_constructDsn($params), + isset($params['charset']) ? $params['charset'] : null, + isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT, + isset($params['persistent']) ? $params['persistent'] : false + ); + } + + /** + * Constructs the Oracle DSN. + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + $dsn = ''; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . '))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . '))'; + } + if (isset($params['pooled']) && $params['pooled'] == true) { + $dsn .= '(SERVER=POOLED)'; + } + $dsn .= ')'; + } else { + $dsn .= $params['dbname']; + } + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'oci8'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php new file mode 100644 index 00000000..bc74787e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Platforms\OraclePlatform; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @var resource + */ + protected $dbh; + + /** + * @var int + */ + protected $executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Create a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + */ + public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT, $persistent = false) + { + if (!defined('OCI_NO_AUTO_COMMIT')) { + define('OCI_NO_AUTO_COMMIT', 0); + } + + $this->dbh = $persistent + ? @oci_pconnect($username, $password, $db, $charset, $sessionMode) + : @oci_connect($username, $password, $db, $charset, $sessionMode); + + if ( ! $this->dbh) { + throw OCI8Exception::fromErrorInfo(oci_error()); + } + } + + /** + * Create a non-executed prepared statement. + * + * @param string $prepareString + * @return OCI8Statement + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->dbh, $prepareString, $this); + } + + /** + * @param string $sql + * @return OCI8Statement + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * Quote input value. + * + * @param mixed $input + * @param int $type PDO::PARAM* + * @return mixed + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * + * @param string $statement + * @return int + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return false; + } + + OraclePlatform::assertValidIdentifier($name); + + $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; + $stmt = $this->query($sql); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + if ($result === false || !isset($result['CURRVAL'])) { + throw new OCI8Exception("lastInsertId failed: Query was executed but no result was returned."); + } + + return (int) $result['CURRVAL']; + } + + /** + * Return the current execution mode. + */ + public function getExecuteMode() + { + return $this->executeMode; + } + + /** + * Start a transactiom + * + * Oracle has to explicitly set the autocommit mode off. That means + * after connection, a commit or rollback there is always automatically + * opened a new transaction. + * + * @return bool + */ + public function beginTransaction() + { + $this->executeMode = OCI_NO_AUTO_COMMIT; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function commit() + { + if (!oci_commit($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function rollBack() + { + if (!oci_rollback($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + public function errorCode() + { + $error = oci_error($this->dbh); + if ($error !== false) { + $error = $error['code']; + } + return $error; + } + + public function errorInfo() + { + return oci_error($this->dbh); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php new file mode 100644 index 00000000..adeb13f0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,30 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\OCI8; + +class OCI8Exception extends \Exception +{ + static public function fromErrorInfo($error) + { + return new self($error['message'], $error['code']); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php new file mode 100644 index 00000000..e98edb55 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,270 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * The OCI8 implementation of the Statement interface. + * + * @since 2.0 + * @author Roman Borschel + */ +class OCI8Statement implements \IteratorAggregate, Statement +{ + /** Statement handle. */ + protected $_dbh; + protected $_sth; + protected $_conn; + protected static $_PARAM = ':param'; + protected static $fetchModeMap = array( + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::PARAM_LOB => OCI_B_BLOB, + PDO::FETCH_COLUMN => OCI_NUM, + ); + protected $_defaultFetchMode = PDO::FETCH_BOTH; + protected $_paramMap = array(); + + /** + * Creates a new OCI8Statement that uses the given connection handle and SQL statement. + * + * @param resource $dbh The connection handle. + * @param string $statement The SQL statement. + */ + public function __construct($dbh, $statement, OCI8Connection $conn) + { + list($statement, $paramMap) = self::convertPositionalToNamedPlaceholders($statement); + $this->_sth = oci_parse($dbh, $statement); + $this->_dbh = $dbh; + $this->_paramMap = $paramMap; + $this->_conn = $conn; + } + + /** + * Convert positional (?) into named placeholders (:param) + * + * Oracle does not support positional parameters, hence this method converts all + * positional parameters into artificially named parameters. Note that this conversion + * is not perfect. All question marks (?) in the original statement are treated as + * placeholders and converted to a named parameter. + * + * The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral. + * Question marks inside literal strings are therefore handled correctly by this method. + * This comes at a cost, the whole sql statement has to be looped over. + * + * @todo extract into utility class in Doctrine\DBAL\Util namespace + * @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements. + * @param string $statement The SQL statement to convert. + * @return string + */ + static public function convertPositionalToNamedPlaceholders($statement) + { + $count = 1; + $inLiteral = false; // a valid query never starts with quotes + $stmtLen = strlen($statement); + $paramMap = array(); + for ($i = 0; $i < $stmtLen; $i++) { + if ($statement[$i] == '?' && !$inLiteral) { + // real positional parameter detected + $paramMap[$count] = ":param$count"; + $len = strlen($paramMap[$count]); + $statement = substr_replace($statement, ":param$count", $i, 1); + $i += $len-1; // jump ahead + $stmtLen = strlen($statement); // adjust statement length + ++$count; + } else if ($statement[$i] == "'" || $statement[$i] == '"') { + $inLiteral = ! $inLiteral; // switch state! + } + } + + return array($statement, $paramMap); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $column = isset($this->_paramMap[$column]) ? $this->_paramMap[$column] : $column; + + if ($type == \PDO::PARAM_LOB) { + $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); + + return oci_bind_by_name($this->_sth, $column, $lob, -1, OCI_B_BLOB); + } else if ($length !== null) { + return oci_bind_by_name($this->_sth, $column, $variable, $length); + } + + return oci_bind_by_name($this->_sth, $column, $variable); + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function closeCursor() + { + return oci_free_statement($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return oci_num_fields($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->_sth); + if ($error !== false) { + $error = $error['code']; + } + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + if ($hasZeroIndex && is_numeric($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode()); + if ( ! $ret) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + return $ret; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + return oci_fetch_array($this->_sth, self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + $result = array(); + if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) { + while ($row = $this->fetch($fetchMode)) { + $result[] = $row; + } + } else { + $fetchStructure = OCI_FETCHSTATEMENT_BY_ROW; + if ($fetchMode == PDO::FETCH_COLUMN) { + $fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN; + } + + oci_fetch_all($this->_sth, $result, 0, -1, + self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS); + + if ($fetchMode == PDO::FETCH_COLUMN) { + $result = $result[0]; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + return isset($row[$columnIndex]) ? $row[$columnIndex] : false; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return oci_num_rows($this->_sth); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php new file mode 100644 index 00000000..4595e5a4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use \PDO; + +/** + * PDO implementation of the Connection interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOConnection extends PDO implements Connection +{ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + parent::__construct($dsn, $user, $password, $options); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Doctrine\DBAL\Driver\PDOStatement', array())); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php new file mode 100644 index 00000000..de307577 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,126 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Connection; + +/** + * Driver for the PDO IBM extension + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'ibm:'; + if (isset($params['host'])) { + $dsn .= 'HOSTNAME=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'PORT=' . $params['port'] . ';'; + } + $dsn .= 'PROTOCOL=TCPIP;'; + if (isset($params['dbname'])) { + $dsn .= 'DATABASE=' . $params['dbname'] . ';'; + } + + return $dsn; + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param \Doctrine\DBAL\Connection $conn + * @return \Doctrine\DBAL\Schema\DB2SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'pdo_ibm'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php new file mode 100644 index 00000000..eeb67279 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\Connection; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + if (isset($params['charset'])) { + $dsn .= 'charset=' . $params['charset'] . ';'; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_mysql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php new file mode 100644 index 00000000..50e98fd6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\Platforms; + +/** + * PDO Oracle driver + * + * WARNING: This driver gives us segfaults in our testsuites on CLOB and other + * stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community, + * which leads us to the recommendation to use the "oci8" driver to connect + * to Oracle instead. + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Oracle PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'oci:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . ')))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . ')))'; + } + } else { + $dsn .= 'dbname=' . $params['dbname']; + } + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'pdo_oracle'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php new file mode 100644 index 00000000..bbe555fd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,73 @@ +_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Postgres PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ' '; + } + if (isset($params['port']) && $params['port'] != '') { + $dsn .= 'port=' . $params['port'] . ' '; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ' '; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\PostgreSqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\PostgreSqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_pgsql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return (isset($params['dbname'])) + ? $params['dbname'] + : $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php new file mode 100644 index 00000000..903d9994 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * @var array + */ + protected $_userDefinedFunctions = array( + 'sqrt' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfSqrt'), 'numArgs' => 1), + 'mod' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfMod'), 'numArgs' => 2), + 'locate' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfLocate'), 'numArgs' => -1), + ); + + /** + * Tries to establish a database connection to SQLite. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return \Doctrine\DBAL\Driver\PDOConnection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (isset($driverOptions['userDefinedFunctions'])) { + $this->_userDefinedFunctions = array_merge( + $this->_userDefinedFunctions, $driverOptions['userDefinedFunctions']); + unset($driverOptions['userDefinedFunctions']); + } + + $pdo = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @return string The DSN. + * @override + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } else if (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * Gets the database platform that is relevant for this driver. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SqlitePlatform(); + } + + /** + * Gets the schema manager that is relevant for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * @return \Doctrine\DBAL\Schema\SqliteSchemaManager + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SqliteSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlite'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return isset($params['path']) ? $params['path'] : null; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php new file mode 100644 index 00000000..01a5769f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @override + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + $val = parent::quote($value, $type); + + // Fix for a driver version terminating all values with null byte + if (strpos($val, "\0") !== false) { + $val = substr($val, 0, -1); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php new file mode 100644 index 00000000..7072b5fa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new Connection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Sqlsrv PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + } + + if (isset($params['port']) && !empty($params['port'])) { + $dsn .= ',' . $params['port']; + } + + if (isset($params['dbname'])) {; + $dsn .= ';Database=' . $params['dbname']; + } + + if (isset($params['MultipleActiveResultSets'])) { + $dsn .= '; MultipleActiveResultSets=' . ($params['MultipleActiveResultSets'] ? 'true' : 'false'); + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SQLServer2008Platform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlsrv'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php new file mode 100644 index 00000000..2673ae9d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * The PDO implementation of the Statement interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOStatement extends \PDOStatement implements Statement +{ + private function __construct() {} + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + // This thin wrapper is necessary to shield against the weird signature + // of PDOStatement::setFetchMode(): even if the second and third + // parameters are optional, PHP will not let us remove it from this + // declaration. + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchMode); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchMode, $arg2); + } + + return parent::setFetchMode($fetchMode, $arg2, $arg3); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php new file mode 100644 index 00000000..9402fab4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,91 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Interface for the reading part of a prepare statement only. + * + * @author Benjamin Eberlei + */ +interface ResultStatement extends \Traversable +{ + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function closeCursor(); + + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + function columnCount(); + + /** + * setFetchMode + * Set the fetch mode to use while iterating this statement. + * + * @param integer $fetchMode + */ + function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return mixed + */ + function fetch($fetchMode = null); + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchMode Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @return array + */ + function fetchAll($fetchMode = null); + + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + function fetchColumn($columnIndex = 0); +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php new file mode 100644 index 00000000..c7043cc6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Driver for ext/sqlsrv + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (!isset($params['host'])) { + throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver."); + } + if (!isset($params['dbname'])) { + throw new SQLSrvException("Missing 'dbname' in configuration for sqlsrv driver."); + } + + $serverName = $params['host']; + if (isset($params['port'])) { + $serverName .= ', ' . $params['port']; + } + $driverOptions['Database'] = $params['dbname']; + $driverOptions['UID'] = $username; + $driverOptions['PWD'] = $password; + + if (!isset($driverOptions['ReturnDatesAsStrings'])) { + $driverOptions['ReturnDatesAsStrings'] = 1; + } + + return new SQLSrvConnection($serverName, $driverOptions); + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SQLServer2008Platform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn); + } + + public function getName() + { + return 'sqlsrv'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php new file mode 100644 index 00000000..421d0715 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Last Id Data Container + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class LastInsertId +{ + private $id; + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php new file mode 100644 index 00000000..82544ca1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * SQL Server implementation for the Connection interface. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @var resource + */ + protected $conn; + + /** + * @var LastInsertId + */ + protected $lastInsertId; + + + public function __construct($serverName, $connectionOptions) + { + $this->conn = sqlsrv_connect($serverName, $connectionOptions); + if ( ! $this->conn) { + throw SQLSrvException::fromSqlSrvErrors(); + } + $this->lastInsertId = new LastInsertId(); + } + + /** + * {@inheritDoc} + */ + public function prepare($sql) + { + return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * {@inheritDoc} + * @license New BSD, code from Zend Framework + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value)) { + return $value; + } else if (is_float($value)) { + return sprintf('%F', $value); + } + + return "'" . str_replace("'", "''", $value) . "'"; + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name !== null) { + $sql = "SELECT IDENT_CURRENT(".$this->quote($name).") AS LastInsertId"; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt->fetchColumn(); + } + + return $this->lastInsertId->getId(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + if ( ! sqlsrv_begin_transaction($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + if ( ! sqlsrv_commit($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + if ( ! sqlsrv_rollback($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + return false; + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php new file mode 100644 index 00000000..f5b208e4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +class SQLSrvException extends \Doctrine\DBAL\DBALException +{ + /** + * Helper method to turn sql server errors into exception. + * + * @return SQLSrvException + */ + static public function fromSqlSrvErrors() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + $message = ""; + foreach ($errors as $error) { + $message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n"; + } + if ( ! $message) { + $message = "SQL Server error occurred but no error message was retrieved from driver."; + } + + return new self(rtrim($message)); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php new file mode 100644 index 00000000..76a156b3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -0,0 +1,251 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * SQL Server Statement + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvStatement implements IteratorAggregate, Statement +{ + /** + * SQLSRV Resource + * + * @var resource + */ + private $conn; + + /** + * SQL Statement to execute + * + * @var string + */ + private $sql; + + /** + * SQLSRV Statement Resource + * + * @var resource + */ + private $stmt; + + /** + * Parameters to bind + * + * @var array + */ + private $params = array(); + + /** + * Translations + * + * @var array + */ + private static $fetchMap = array( + PDO::FETCH_BOTH => SQLSRV_FETCH_BOTH, + PDO::FETCH_ASSOC => SQLSRV_FETCH_ASSOC, + PDO::FETCH_NUM => SQLSRV_FETCH_NUMERIC, + ); + + /** + * Fetch Style + * + * @param int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var int|null + */ + private $lastInsertId; + + /** + * Append to any INSERT query to retrieve the last insert id. + * + * @var string + */ + const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; + + public function __construct($conn, $sql, $lastInsertId = null) + { + $this->conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') === 0) { + $this->sql .= self::LAST_INSERT_ID_SQL; + $this->lastInsertId = $lastInsertId; + } + } + + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type,null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (!is_numeric($column)) { + throw new SQLSrvException("sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead."); + } + + if ($type === \PDO::PARAM_LOB) { + $this->params[$column-1] = array($variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max')); + } else { + $this->params[$column-1] = $variable; + } + } + + public function closeCursor() + { + if ($this->stmt) { + sqlsrv_free_stmt($this->stmt); + } + } + + public function columnCount() + { + return sqlsrv_num_fields($this->stmt); + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + return false; + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } + + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + $this->bindValue($key, $val); + } + } + + $this->stmt = sqlsrv_query($this->conn, $this->sql, $this->params); + if ( ! $this->stmt) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + if ($this->lastInsertId) { + sqlsrv_next_result($this->stmt); + sqlsrv_fetch($this->stmt); + $this->lastInsertId->setId( sqlsrv_get_field($this->stmt, 0) ); + } + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if (isset(self::$fetchMap[$fetchMode])) { + return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]); + } else if ($fetchMode == PDO::FETCH_OBJ || $fetchMode == PDO::FETCH_CLASS) { + $className = null; + $ctorArgs = null; + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs); + } + + throw new SQLSrvException("Fetch mode is not supported!"); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $className = null; + $ctorArgs = null; + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + + $rows = array(); + while ($row = $this->fetch($fetchMode, $className, $ctorArgs)) { + $rows[] = $row; + } + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + return $row[$columnIndex]; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sqlsrv_rows_affected($this->stmt); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php new file mode 100644 index 00000000..a4543e25 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + /** + * Binds a PHP variable to a corresponding named or question mark placeholder in the + * SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that PDOStatement->execute() is called. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $column Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * @param integer $length You must specify maxlength when using an OUT bind so that PHP allocates enough memory to hold the returned value. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null, $length = null); + + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorCode() + * @return string error code string + */ + function errorCode(); + + /** + * errorInfo + * Fetch extended error information associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorInfo() + * @return array error info array + */ + function errorInfo(); + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values + * + * + * @param array $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + function rowCount(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php new file mode 100644 index 00000000..0f196261 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,176 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\Common\EventManager; + +/** + * Factory for creating Doctrine\DBAL\Connection instances. + * + * @author Roman Borschel + * @since 2.0 + */ +final class DriverManager +{ + /** + * List of supported drivers and their mappings to the driver classes. + * + * To add your own driver use the 'driverClass' parameter to + * {@link DriverManager::getConnection()}. + * + * @var array + */ + private static $_driverMap = array( + 'pdo_mysql' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', + 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', + 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', + 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', + 'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', + 'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', + 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + ); + + /** Private constructor. This class cannot be instantiated. */ + private function __construct() { } + + /** + * Creates a connection object based on the specified parameters. + * This method returns a Doctrine\DBAL\Connection which wraps the underlying + * driver connection. + * + * $params must contain at least one of the following. + * + * Either 'driver' with one of the following values: + * + * pdo_mysql + * pdo_sqlite + * pdo_pgsql + * pdo_oci (unstable) + * pdo_sqlsrv + * pdo_ibm (unstable) + * pdo_sqlsrv + * mysqli + * sqlsrv + * ibm_db2 (unstable) + * drizzle_pdo_mysql + * + * OR 'driverClass' that contains the full class name (with namespace) of the + * driver class to instantiate. + * + * Other (optional) parameters: + * + * user (string): + * The username to use when connecting. + * + * password (string): + * The password to use when connecting. + * + * driverOptions (array): + * Any additional driver-specific options for the driver. These are just passed + * through to the driver. + * + * pdo: + * You can pass an existing PDO instance through this parameter. The PDO + * instance will be wrapped in a Doctrine\DBAL\Connection. + * + * wrapperClass: + * You may specify a custom wrapper class through the 'wrapperClass' + * parameter but this class MUST inherit from Doctrine\DBAL\Connection. + * + * driverClass: + * The driver class to use. + * + * @param array $params The parameters. + * @param \Doctrine\DBAL\Configuration The configuration to use. + * @param \Doctrine\Common\EventManager The event manager to use. + * @return \Doctrine\DBAL\Connection + */ + public static function getConnection( + array $params, + Configuration $config = null, + EventManager $eventManager = null) + { + // create default config and event manager, if not set + if ( ! $config) { + $config = new Configuration(); + } + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } else if (isset($params['pdo'])) { + $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + self::_checkParams($params); + } + if (isset($params['driverClass'])) { + $className = $params['driverClass']; + } else { + $className = self::$_driverMap[$params['driver']]; + } + + $driver = new $className(); + + $wrapperClass = 'Doctrine\DBAL\Connection'; + if (isset($params['wrapperClass'])) { + if (is_subclass_of($params['wrapperClass'], $wrapperClass)) { + $wrapperClass = $params['wrapperClass']; + } else { + throw DBALException::invalidWrapperClass($params['wrapperClass']); + } + } + + return new $wrapperClass($params, $driver, $config, $eventManager); + } + + /** + * Checks the list of parameters. + * + * @param array $params + */ + private static function _checkParams(array $params) + { + // check existence of mandatory parameters + + // driver + if ( ! isset($params['driver']) && ! isset($params['driverClass'])) { + throw DBALException::driverRequired(); + } + + // check validity of parameters + + // driver + if ( isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) { + throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap)); + } + + if (isset($params['driverClass']) && ! in_array('Doctrine\DBAL\Driver', class_implements($params['driverClass'], true))) { + throw DBALException::invalidDriverClass($params['driverClass']); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php new file mode 100644 index 00000000..f4cb1cd8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,79 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs, + Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var Connection + */ + private $_connection = null; + + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_connection->getDriver(); + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } + + /** + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + return $this->_connection->getSchemaManager(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php new file mode 100644 index 00000000..fc22744a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,74 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * MySQL Session Init Event Subscriber which allows to set the Client Encoding of the Connection + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * @var string + */ + private $_charset; + + /** + * @var string + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection + * + * @param string $charset + * @param string $collation + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php new file mode 100644 index 00000000..160ea119 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,80 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Should be used when Oracle Server default environment does not match the Doctrine requirements. + * + * The following environment variables are required for the Doctrine default date format: + * + * NLS_TIME_FORMAT="HH24:MI:SS" + * NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + protected $_defaultSessionVars = array( + 'NLS_TIME_FORMAT' => "HH24:MI:SS", + 'NLS_DATE_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_TZ_FORMAT' => "YYYY-MM-DD HH24:MI:SS TZH:TZM", + 'NLS_NUMERIC_CHARACTERS' => ".,", + ); + + /** + * @param array $oracleSessionVars + */ + public function __construct(array $oracleSessionVars = array()) + { + $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + if (count($this->_defaultSessionVars)) { + array_change_key_case($this->_defaultSessionVars, \CASE_UPPER); + $vars = array(); + foreach ($this->_defaultSessionVars as $option => $value) { + $vars[] = $option." = '".$value."'"; + } + $sql = "ALTER SESSION SET ".implode(" ", $vars); + $args->getConnection()->executeUpdate($sql); + } + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php new file mode 100644 index 00000000..8dfde625 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,63 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Session init listener for executing a single SQL statement right after a connection is opened. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + */ +class SQLSessionInit implements EventSubscriber +{ + /** + * @var string + */ + protected $sql; + + /** + * @param string $sql + */ + public function __construct($sql) + { + $this->sql = $sql; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php new file mode 100644 index 00000000..0200ce77 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php new file mode 100644 index 00000000..bd59d7d7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_columnDiff = $columnDiff; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\ColumnDiff + */ + public function getColumnDiff() + { + return $this->_columnDiff; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php new file mode 100644 index 00000000..9f853388 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,99 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php new file mode 100644 index 00000000..4b981f89 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php new file mode 100644 index 00000000..90e6a385 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -0,0 +1,129 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName = null; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_oldColumnName = $oldColumnName; + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return string + */ + public function getOldColumnName() + { + return $this->_oldColumnName; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php new file mode 100644 index 00000000..fecb0152 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * Raw column data as fetched from the database + * + * @var array + */ + private $_tableColumn = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var string + */ + private $_database = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(array $tableColumn, $table, $database, Connection $connection) + { + $this->_tableColumn = $tableColumn; + $this->_table = $table; + $this->_database = $database; + $this->_connection = $connection; + } + + /** + * Allows to clear the column which means the column will be excluded from + * tables column list. + * + * @param null|\Doctrine\DBAL\Schema\Column $column + * @return SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return array + */ + public function getTableColumn() + { + return $this->_tableColumn; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->_database; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php new file mode 100644 index 00000000..5e7383c0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, Table $table, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php new file mode 100644 index 00000000..3149faa7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php @@ -0,0 +1,128 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var array + */ + private $_columns = null; + + /** + * @var array + */ + private $_options = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param array $columns + * @param array $options + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Table $table, array $columns, array $options, AbstractPlatform $platform) + { + $this->_table = $table; + $this->_columns = $columns; + $this->_options = $options; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php new file mode 100644 index 00000000..55133be2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,98 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var string + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($table, AbstractPlatform $platform) + { + if ( ! $table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaCreateTableEventArgs expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return string|\Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string $sql + * @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs + */ + public function setSql($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php new file mode 100644 index 00000000..a3509fb2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,56 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaEventArgs extends EventArgs +{ + /** + * @var boolean + */ + private $_preventDefault = false; + + /** + * @return \Doctrine\DBAL\Event\SchemaEventArgs + */ + public function preventDefault() + { + $this->_preventDefault = true; + + return $this; + } + + /** + * @return boolean + */ + public function isDefaultPrevented() + { + return $this->_preventDefault; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php new file mode 100644 index 00000000..248d43e9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,122 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index + */ + private $_index = null; + + /** + * Raw index data as fetched from the database + * + * @var array + */ + private $_tableIndex = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(array $tableIndex, $table, Connection $connection) + { + $this->_tableIndex = $tableIndex; + $this->_table = $table; + $this->_connection = $connection; + } + + /** + * Allows to clear the index which means the index will be excluded from + * tables index list. + * + * @param null|\Doctrine\DBAL\Schema\Index $index + * @return SchemaIndexDefinitionEventArgs + */ + public function setIndex(Index $index = null) + { + $this->_index = $index; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Index + */ + public function getIndex() + { + return $this->_index; + } + + /** + * @return array + */ + public function getTableIndex() + { + return $this->_tableIndex; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php new file mode 100644 index 00000000..0869dd97 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + private function __construct() {} + + const postConnect = 'postConnect'; + + const onSchemaCreateTable = 'onSchemaCreateTable'; + const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; + const onSchemaDropTable = 'onSchemaDropTable'; + const onSchemaAlterTable = 'onSchemaAlterTable'; + const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn'; + const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn'; + const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; + const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; + const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php new file mode 100644 index 00000000..9e76966a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php @@ -0,0 +1,160 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Connection; + +/** + * Table ID Generator for those poor languages that are missing sequences. + * + * WARNING: The Table Id Generator clones a second independent database + * connection to work correctly. This means using the generator requests that + * generate IDs will have two open database connections. This is necessary to + * be safe from transaction failures in the main connection. Make sure to only + * ever use one TableGenerator otherwise you end up with many connections. + * + * TableID Generator does not work with SQLite. + * + * The TableGenerator does not take care of creating the SQL Table itself. You + * should look at the `TableGeneratorSchemaVisitor` to do this for you. + * Otherwise the schema for a table looks like: + * + * CREATE sequences ( + * sequence_name VARCHAR(255) NOT NULL, + * sequence_value INT NOT NULL DEFAULT '1', + * sequence_increment_by INT NOT NULL DEFAULT '1', + * PRIMARY KEY (table_name) + * ); + * + * Technically this generator works as follows: + * + * 1. Use a robust transaction serialization level. + * 2. Open transaction + * 3. Acquire a read lock on the table row (SELECT .. FOR UPDATE) + * 4. Increment current value by one and write back to database + * 5. Commit transaction + * + * If you are using a sequence_increment_by value that is larger than one the + * ID Generator will keep incrementing values until it hits the incrementation + * gap before issuing another query. + * + * If no row is present for a given sequence a new one will be created with the + * default values 'value' = 1 and 'increment_by' = 1 + * + * @author Benjamin Eberlei + */ +class TableGenerator +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $generatorTableName; + + /** + * @var array + */ + private $sequences = array(); + + /** + * @param Connection $conn + * @param string $generatorTableName + */ + public function __construct(Connection $conn, $generatorTableName = 'sequences') + { + $params = $conn->getParams(); + if ($params['driver'] == 'pdo_sqlite') { + throw new \Doctrine\DBAL\DBALException("Cannot use TableGenerator with SQLite."); + } + $this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager()); + $this->generatorTableName = $generatorTableName; + } + + /** + * Generate the next unused value for the given sequence name + * + * @param string + * @return int + */ + public function nextValue($sequenceName) + { + if (isset($this->sequences[$sequenceName])) { + $value = $this->sequences[$sequenceName]['value']; + $this->sequences[$sequenceName]['value']++; + if ($this->sequences[$sequenceName]['value'] >= $this->sequences[$sequenceName]['max']) { + unset ($this->sequences[$sequenceName]); + } + return $value; + } + + $this->conn->beginTransaction(); + + try { + $platform = $this->conn->getDatabasePlatform(); + $sql = "SELECT sequence_value, sequence_increment_by " . + "FROM " . $platform->appendLockHint($this->generatorTableName, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) . " " . + "WHERE sequence_name = ? " . $platform->getWriteLockSQL(); + $stmt = $this->conn->executeQuery($sql, array($sequenceName)); + + if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row = array_change_key_case($row, CASE_LOWER); + + $value = $row['sequence_value']; + $value++; + + if ($row['sequence_increment_by'] > 1) { + $this->sequences[$sequenceName] = array( + 'value' => $value, + 'max' => $row['sequence_value'] + $row['sequence_increment_by'] + ); + } + + $sql = "UPDATE " . $this->generatorTableName . " ". + "SET sequence_value = sequence_value + sequence_increment_by " . + "WHERE sequence_name = ? AND sequence_value = ?"; + $rows = $this->conn->executeUpdate($sql, array($sequenceName, $row['sequence_value'])); + + if ($rows != 1) { + throw new \Doctrine\DBAL\DBALException("Race-condition detected while updating sequence. Aborting generation"); + } + } else { + $this->conn->insert( + $this->generatorTableName, + array('sequence_name' => $sequenceName, 'sequence_value' => 1, 'sequence_increment_by' => 1) + ); + $value = 1; + } + + $this->conn->commit(); + + } catch(\Exception $e) { + $this->conn->rollback(); + throw new \Doctrine\DBAL\DBALException("Error occurred while generating ID with TableGenerator, aborted generation: " . $e->getMessage(), 0, $e); + } + + return $value; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php new file mode 100644 index 00000000..e3403976 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class TableGeneratorSchemaVisitor implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + /** + * @var string + */ + private $generatorTableName; + + public function __construct($generatorTableName = 'sequences') + { + $this->generatorTableName = $generatorTableName; + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + $table = $schema->createTable($this->generatorTableName); + $table->addColumn('sequence_name', 'string'); + $table->addColumn('sequence_value', 'integer', array('default' => 1)); + $table->addColumn('sequence_increment_by', 'integer', array('default' => 1)); + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php new file mode 100644 index 00000000..52d87d23 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php @@ -0,0 +1,42 @@ +. +*/ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + final private function __construct() { } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php new file mode 100644 index 00000000..7d70b907 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** @var array $queries Executed SQL queries. */ + public $queries = array(); + + /** @var boolean $enabled If Debug Stack is enabled (log queries) or not. */ + public $enabled = true; + + public $start = null; + + public $currentQuery = 0; + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if ($this->enabled) { + $this->start = microtime(true); + $this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + if ($this->enabled) { + $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; + } + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php new file mode 100644 index 00000000..a332258f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * A SQL logger that logs to the standard output using echo/var_dump. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EchoSQLLogger implements SQLLogger +{ + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + echo $sql . PHP_EOL; + + if ($params) { + var_dump($params); + } + + if ($types) { + var_dump($types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php new file mode 100644 index 00000000..6725cc5e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + private $loggers = array(); + + /** + * Adds a logger in the chain + * + * @param SQLLogger $logger + */ + public function addLogger(SQLLogger $logger) + { + $this->loggers[] = $logger; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + foreach ($this->loggers as $logger) { + $logger->startQuery($sql, $params, $types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + foreach ($this->loggers as $logger) { + $logger->stopQuery(); + } + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php new file mode 100644 index 00000000..9564f4c9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface SQLLogger +{ + /** + * Logs a SQL statement somewhere. + * + * @param string $sql The SQL to be executed. + * @param array $params The SQL parameters. + * @param array $types The SQL parameter types. + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Mark the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php new file mode 100644 index 00000000..204d13da --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,2844 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Events, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\SchemaCreateTableEventArgs, + Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs, + Doctrine\DBAL\Event\SchemaDropTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs; + +/** + * Base class for all DatabasePlatforms. The DatabasePlatforms are the central + * point of abstraction of platform-specific behaviors, features and SQL dialects. + * They are a passive source of information. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Remove any unnecessary methods. + */ +abstract class AbstractPlatform +{ + /** + * @var integer + */ + const CREATE_INDEXES = 1; + + /** + * @var integer + */ + const CREATE_FOREIGNKEYS = 2; + + /** + * @var integer + */ + const TRIM_UNSPECIFIED = 0; + + /** + * @var integer + */ + const TRIM_LEADING = 1; + + /** + * @var integer + */ + const TRIM_TRAILING = 2; + + /** + * @var integer + */ + const TRIM_BOTH = 3; + + /** + * @var array + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array + */ + protected $doctrineTypeComments = null; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * Holds the KeywordList instance for the current platform. + * + * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + protected $_keywords; + + /** + * Constructor. + */ + public function __construct() {} + + /** + * Sets the EventManager used by the Platform. + * + * @param \Doctrine\Common\EventManager + */ + public function setEventManager(EventManager $eventManager) + { + $this->_eventManager = $eventManager; + } + + /** + * Gets the EventManager used by the Platform. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * @return string + */ + abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Lazy load Doctrine Type Mappings + * + * @return void + */ + abstract protected function initializeDoctrineTypeMappings(); + + /** + * Initialize Doctrine Type Mappings with the platform defaults + * and with all additional type mappings. + */ + private function initializeAllDoctrineTypeMappings() + { + $this->initializeDoctrineTypeMappings(); + + foreach (Type::getTypesMap() as $typeName => $className) { + foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { + $this->doctrineTypeMapping[$dbType] = $typeName; + } + } + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + * + * @return string + */ + public function getVarcharTypeDeclarationSQL(array $field) + { + if ( !isset($field['length'])) { + $field['length'] = $this->getVarcharDefaultLength(); + } + + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + if ($field['length'] > $this->getVarcharMaxLength()) { + return $this->getClobTypeDeclarationSQL($field); + } + + return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Get the SQL Snippet to create a GUID/UUID field. + * + * By default this maps directly to a VARCHAR and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return $this->getVarcharTypeDeclarationSQL($field); + } + + /** + * @param integer $length + * @param boolean $fixed + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getBlobTypeDeclarationSQL(array $field); + + /** + * Gets the name of the platform. + * + * @return string + */ + abstract public function getName(); + + /** + * Register a doctrine type to be used in conjunction with a column type of this platform. + * + * @param string $dbType + * @param string $doctrineType + * + * @throws \Doctrine\DBAL\DBALException if the type is not found + */ + public function registerDoctrineTypeMapping($dbType, $doctrineType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + if (!Types\Type::hasType($doctrineType)) { + throw DBALException::typeNotFound($doctrineType); + } + + $dbType = strtolower($dbType); + $this->doctrineTypeMapping[$dbType] = $doctrineType; + } + + /** + * Get the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * + * @return string + */ + public function getDoctrineTypeMapping($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + if (!isset($this->doctrineTypeMapping[$dbType])) { + throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); + } + + return $this->doctrineTypeMapping[$dbType]; + } + + /** + * Check if a database type is currently supported by this platform. + * + * @param string $dbType + * + * @return boolean + */ + public function hasDoctrineTypeMappingFor($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + return isset($this->doctrineTypeMapping[$dbType]); + } + + /** + * Initialize the Doctrine Type comments instance variable for in_array() checks. + * + * @return void + */ + protected function initializeCommentedDoctrineTypes() + { + $this->doctrineTypeComments = array(); + + foreach (Type::getTypesMap() as $typeName => $className) { + $type = Type::getType($typeName); + + if ($type->requiresSQLCommentHint($this)) { + $this->doctrineTypeComments[] = $typeName; + } + } + } + + /** + * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? + * + * @param Type $doctrineType + * + * @return boolean + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Mark this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param string|Type $doctrineType + * + * @return void + */ + public function markDoctrineTypeCommented($doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + } + + /** + * Get the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param Type $doctrineType + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Return the comment of a passed column modified by potential doctrine type comment hints. + * + * @param Column $column + * @return string + */ + protected function getColumnComment(Column $column) + { + $comment = $column->getComment(); + + if ($this->isCommentedDoctrineType($column->getType())) { + $comment .= $this->getDoctrineTypeComment($column->getType()); + } + + return $comment; + } + + /** + * Gets the character used for identifier quoting. + * + * @return string + */ + public function getIdentifierQuoteCharacter() + { + return '"'; + } + + /** + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentStartString() + { + return "--"; + } + + /** + * Gets the string portion that ends an SQL comment. + * + * @return string + */ + public function getSqlCommentEndString() + { + return "\n"; + } + + /** + * Gets the maximum length of a varchar field. + * + * @return integer + */ + public function getVarcharMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a varchar field. + * + * @return integer + */ + public function getVarcharDefaultLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the average value of a column + * + * @param string $column the column to use + * + * @return string generated sql including an AVG aggregate function + */ + public function getAvgExpression($column) + { + return 'AVG(' . $column . ')'; + } + + /** + * Returns the number of rows (without a NULL value) of a column + * + * If a '*' is used instead of a column the number of selected rows + * is returned. + * + * @param string|integer $column the column to use + * + * @return string generated sql including a COUNT aggregate function + */ + public function getCountExpression($column) + { + return 'COUNT(' . $column . ')'; + } + + /** + * Returns the highest value of a column + * + * @param string $column the column to use + * @return string generated sql including a MAX aggregate function + */ + public function getMaxExpression($column) + { + return 'MAX(' . $column . ')'; + } + + /** + * Returns the lowest value of a column + * + * @param string $column the column to use + * @return string + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the total sum of a column + * + * @param string $column the column to use + * @return string + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the md5 sum of a field. + * + * Note: Not SQL92, but common functionality + * + * @param string $column + * @return string + */ + public function getMd5Expression($column) + { + return 'MD5(' . $column . ')'; + } + + /** + * Returns the length of a text field. + * + * @param string $column + * + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Returns the squared value of a column + * + * @param string $column the column to use + * + * @return string generated sql including an SQRT aggregate function + */ + public function getSqrtExpression($column) + { + return 'SQRT(' . $column . ')'; + } + + /** + * Rounds a numeric field to the number of decimals specified. + * + * @param string $column + * @param integer $decimals + * + * @return string + */ + public function getRoundExpression($column, $decimals = 0) + { + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the remainder of the division operation + * $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * + * @return string + */ + public function getModExpression($expression1, $expression2) + { + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + + /** + * Trim a string, leading/trailing/both and with a given char which defaults to space. + * + * @param string $str + * @param integer $pos + * @param string $char has to be quoted already + * + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $posStr = ''; + $trimChar = ($char != false) ? $char . ' FROM ' : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $posStr = 'LEADING '.$trimChar; + break; + + case self::TRIM_TRAILING: + $posStr = 'TRAILING '.$trimChar; + break; + + case self::TRIM_BOTH: + $posStr = 'BOTH '.$trimChar; + break; + } + + return 'TRIM(' . $posStr . $str . ')'; + } + + /** + * rtrim + * returns the string $str with proceeding space characters removed + * + * @param string $str literal string or column name + * + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * ltrim + * returns the string $str with leading space characters removed + * + * @param string $str literal string or column name + * + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * upper + * Returns the string $str with all characters changed to + * uppercase according to the current character set mapping. + * + * @param string $str literal string or column name + * + * @return string + */ + public function getUpperExpression($str) + { + return 'UPPER(' . $str . ')'; + } + + /** + * lower + * Returns the string $str with all characters changed to + * lowercase according to the current character set mapping. + * + * @param string $str literal string or column name + * + * @return string + */ + public function getLowerExpression($str) + { + return 'LOWER(' . $str . ')'; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $str literal string + * @param string $substr literal string to find + * @param integer $startPos position to start at, beginning of string by default + * + * @return string + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function + * + * @param string $value an sql string literal or column name/alias + * @param integer $from where to start the substring portion + * @param integer $length the substring portion length + * + * @return string + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * Returns a series of strings concatenated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression + * + * @param string $arg1, $arg2 ... $argN strings that will be concatenated. + * + * @return string + */ + public function getConcatExpression() + { + return join(' || ' , func_get_args()); + } + + /** + * Returns the SQL for a logical not. + * + * Example: + * + * $q = new Doctrine_Query(); + * $e = $q->expr; + * $q->select('*')->from('table') + * ->where($e->eq('id', $e->not('null')); + * + * + * @param string $expression + * + * @return string a logical expression + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL to check if a value is one in a set of + * given values. + * + * in() accepts an arbitrary number of parameters. The first parameter + * must always specify the value that should be matched against. Successive + * must contain a logical expression or an array with logical expressions. + * These expressions will be matched against the first parameter. + * + * @param string $column the value that should be matched against + * @param string|array $values values that will be matched against $column + * + * @return string logical expression + */ + public function getInExpression($column, $values) + { + if ( ! is_array($values)) { + $values = array($values); + } + + // TODO: fix this code: the method does not exist + $values = $this->getIdentifiers($values); + + if (count($values) == 0) { + throw new \InvalidArgumentException('Values must not be empty.'); + } + + return $column . ' IN (' . implode(', ', $values) . ')'; + } + + /** + * Returns SQL that checks if a expression is null. + * + * @param string $expression the expression that should be compared to null + * + * @return string logical expression + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns SQL that checks if a expression is not null. + * + * @param string $expression the expression that should be compared to null + * + * @return string logical expression + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns SQL that checks if an expression evaluates to a value between + * two values. + * + * The parameter $expression is checked if it is between $value1 and $value2. + * + * Note: There is a slight difference in the way BETWEEN works on some databases. + * http://www.w3schools.com/sql/sql_between.asp. If you want complete database + * independence you should avoid using between(). + * + * @param string $expression the value to compare to + * @param string $value1 the lower value to compare with + * @param string $value2 the higher value to compare with + * + * @return string logical expression + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + public function getPiExpression() + { + return 'PI()'; + } + + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2 + * + * @param string $date1 + * @param string $date2 + * + * @return string + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + */ + public function getDateAddDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + */ + public function getDateSubDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + */ + public function getDateAddMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + */ + public function getDateSubMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets SQL bit AND comparison expression + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Gets SQL bit OR comparison expression + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + public function getForUpdateSQL() + { + return 'FOR UPDATE'; + } + + /** + * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification. + * + * @param string $fromClause + * @param integer $lockMode + * + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Get the sql snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database + * vendors allow to lighten this constraint up to be a real read lock. + * + * @return string + */ + public function getReadLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Get the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. + * + * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Get the SQL snippet to drop an existing database + * + * @param string $database name of the database that should be dropped + * + * @return string + */ + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Drop a Table + * + * @throws \InvalidArgumentException + * + * @param Table|string $table + * + * @return string + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { + $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return $eventArgs->getSql(); + } + } + + return 'DROP TABLE ' . $table; + } + + /** + * Get SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param Table|string $table + * + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Drop index from a table + * + * @param Index|string $name + * @param string|Table $table + * + * @return string + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } else if(!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Get drop constraint sql + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param string|Table $table + * + * @return string + */ + public function getDropConstraintSQL($constraint, $table) + { + if ($constraint instanceof Constraint) { + $constraint = $constraint->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + } + + /** + * @param ForeignKeyConstraint|string $foreignKey + * @param Table|string $table + * + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; + } + + /** + * Gets the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param string $table The name of the table. + * @param integer $createFlags + * + * @return array The sequence of SQL statements. + */ + public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) + { + if ( ! is_int($createFlags)) { + throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer."); + } + + if (count($table->getColumns()) === 0) { + throw DBALException::noColumnsSpecifiedForTable($table->getName()); + } + + $tableName = $table->getQuotedName($this); + $options = $table->getOptions(); + $options['uniqueConstraints'] = array(); + $options['indexes'] = array(); + $options['primary'] = array(); + + if (($createFlags&self::CREATE_INDEXES) > 0) { + foreach ($table->getIndexes() as $index) { + /* @var $index Index */ + if ($index->isPrimary()) { + $platform = $this; + $options['primary'] = array_map(function ($columnName) use ($table, $platform) { + return $table->getColumn($columnName)->getQuotedName($platform); + }, $index->getColumns()); + $options['primary_index'] = $index; + } else { + $options['indexes'][$index->getName()] = $index; + } + } + } + + $columnSql = array(); + $columns = array(); + + foreach ($table->getColumns() as $column) { + /* @var \Doctrine\DBAL\Schema\Column $column */ + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { + $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + if ($eventArgs->isDefaultPrevented()) { + continue; + } + } + + $columnData = $column->toArray(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; + $columnData['comment'] = $this->getColumnComment($column); + + if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + + if (in_array($column->getName(), $options['primary'])) { + $columnData['primary'] = true; + } + + $columns[$columnData['name']] = $columnData; + } + + if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = array(); + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { + $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return array_merge($eventArgs->getSql(), $columnSql); + } + } + + $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + if ($this->supportsCommentOnStatement()) { + foreach ($table->getColumns() as $column) { + if ($this->getColumnComment($column)) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getName(), $this->getColumnComment($column)); + } + } + } + + return array_merge($sql, $columnSql); + } + + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + return "COMMENT ON COLUMN " . $tableName . "." . $columnName . " IS '" . $comment . "'"; + } + + /** + * Gets the SQL used to create a table. + * + * @param string $tableName + * @param array $columns + * @param array $options + * + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if ( ! empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Gets the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws DBALException + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL statement to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL to create a constraint on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param string|Table $table + * + * @return string + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columns = array(); + foreach ($constraint->getColumns() as $column) { + $columns[] = $column; + } + $columnList = '('. implode(', ', $columns) . ')'; + + $referencesClause = ''; + if ($constraint instanceof Index) { + if($constraint->isPrimary()) { + $query .= ' PRIMARY KEY'; + } elseif ($constraint->isUnique()) { + $query .= ' UNIQUE'; + } else { + throw new \InvalidArgumentException( + 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' + ); + } + } else if ($constraint instanceof ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $foreignColumns = array(); + foreach ($constraint->getForeignColumns() as $column) { + $foreignColumns[] = $column; + } + + $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . + ' (' . implode(', ', $foreignColumns) . ')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Gets the SQL to create an index on a table on this platform. + * + * @param Index $index + * @param string|Table $table name of the table on which the index is to be created + * + * @return string + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getColumns(); + + if (count($columns) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; + + return $query; + } + + /** + * Adds additional flags for index generation + * + * @param Index $index + * + * @return string + */ + protected function getCreateIndexSQLFlags(Index $index) + { + return $index->isUnique() ? 'UNIQUE ' : ''; + } + + /** + * Get SQL to create an unnamed primary key constraint. + * + * @param Index $index + * @param string|Table $table + * + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; + } + + /** + * Quotes a string so that it can be safely used as a table or column name, + * even if it is a reserved word of the platform. This also detects identifier + * chains separated by dot and quotes them independently. + * + * NOTE: Just because you CAN use quoted identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteIdentifier"), explode(".", $str)); + + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quote a single identifier (no dot chain separation) + * + * @param string $str + * + * @return string + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table name of the table on which the foreign key is to be created + * + * @return string + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); + + return $query; + } + + /** + * Gets the sql statements for altering an existing table. + * + * The method returns an array of sql statements, since some platforms need several statements. + * + * @param TableDiff $diff + * + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param ColumnDiff $columnDiff + * @param TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param string $oldColumnName + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param TableDiff $diff + * @param array $sql + * + * @return boolean + */ + protected function onSchemaAlterTable(TableDiff $diff, &$sql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { + return false; + } + + $eventArgs = new SchemaAlterTableEventArgs($diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); + + $sql = array_merge($sql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->name; + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->removedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->removedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + + return $sql; + } + + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = false !== $diff->newName ? $diff->newName : $diff->name; + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->addedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->addedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + return $sql; + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param TableDiff $diff + * + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Get declaration of a number of fields in bulk + * + * @param array $fields a multidimensional associative array. + * The first dimension determines the field name, while the second + * dimension is keyed with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * + * @return string + */ + public function getColumnDeclarationListSQL(array $fields) + { + $queryFields = array(); + + foreach ($fields as $fieldName => $field) { + $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field); + } + + return implode(', ', $queryFields); + } + + /** + * Obtain DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * check + * column check constraint + * columnDefinition + * a string that defines the complete column + * + * @return string DBMS specific SQL code portion that should be used to declare the column. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $charset = (isset($field['charset']) && $field['charset']) ? + ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : ''; + + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; + } + + if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment']) { + $columnDef .= " COMMENT '" . $field['comment'] . "'"; + } + + return $name . ' ' . $columnDef; + } + + /** + * Gets the SQL snippet that declares a floating point column of arbitrary precision. + * + * @param array $columnDef + * + * @return string + */ + public function getDecimalTypeDeclarationSQL(array $columnDef) + { + $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision'])) + ? 10 : $columnDef['precision']; + $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale'])) + ? 0 : $columnDef['scale']; + + return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field field definition array + * + * @return string DBMS specific SQL code portion needed to set a default value + */ + public function getDefaultValueDeclarationSQL($field) + { + $default = empty($field['notnull']) ? ' DEFAULT NULL' : ''; + + if (isset($field['default'])) { + $default = " DEFAULT '".$field['default']."'"; + if (isset($field['type'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $default = " DEFAULT ".$field['default']; + } else if ((string)$field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { + $default = " DEFAULT ".$this->getCurrentTimestampSQL(); + } else if ((string)$field['type'] == 'Time' && $field['default'] == $this->getCurrentTimeSQL()) { + $default = " DEFAULT ".$this->getCurrentTimeSQL(); + } else if ((string)$field['type'] == 'Date' && $field['default'] == $this->getCurrentDateSQL()) { + $default = " DEFAULT ".$this->getCurrentDateSQL(); + } else if ((string) $field['type'] == 'Boolean') { + $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + } + } + return $default; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition check definition + * + * @return string DBMS specific SQL code portion needed to set a CHECK constraint + */ + public function getCheckDeclarationSQL(array $definition) + { + $constraints = array(); + foreach ($definition as $field => $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; + } + + if (isset($def['max'])) { + $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; + } + } + } + + return implode(', ', $constraints); + } + + /** + * Obtain DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the unique constraint + * @param Index $index index definition + * + * @return string DBMS specific SQL code portion needed + * to set a constraint + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if (count($index->getColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the index + * @param Index $index index definition + * + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclarationSQL($name, Index $index) + { + if (count($index->getColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * getCustomTypeDeclarationSql + * Obtain SQL code portion needed to create a custom column, + * e.g. when a field has the "columnDefinition" keyword. + * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. + * + * @param array $columnDef + * + * @return string + */ + public function getCustomTypeDeclarationSQL(array $columnDef) + { + return $columnDef['columnDefinition']; + } + + /** + * getIndexFieldDeclarationList + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param array $fields + * + * @return string + */ + public function getIndexFieldDeclarationListSQL(array $fields) + { + $ret = array(); + + foreach ($fields as $field => $definition) { + if (is_array($definition)) { + $ret[] = $field; + } else { + $ret[] = $definition; + } + } + + return implode(', ', $ret); + } + + /** + * A method to return the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * Should be overridden in driver classes to return the correct string for the + * specific database type. + * + * The default is to return the string "TEMPORARY" - this will result in a + * SQL error for any database that does not support temporary tables, or that + * requires a different SQL command from "CREATE TEMPORARY TABLE". + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableSQL() + { + return 'TEMPORARY'; + } + + /** + * Some vendors require temporary table names to be qualified specially. + * + * @param string $tableName + * + * @return string + */ + public function getTemporaryTableName($tableName) + { + return $tableName; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration. + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); + $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $sql; + } + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param ForeignKeyConstraint $foreignKey foreign key definition + * + * @return string + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); + } + if ($foreignKey->hasOption('onDelete')) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + return $query; + } + + /** + * returns given referential action in uppercase if valid, otherwise throws + * an exception + * + * @throws \InvalidArgumentException if unknown referential action given + * + * @param string $action foreign key referential action + * + * @return string + */ + public function getForeignKeyReferentialActionSQL($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + default: + throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); + } + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param ForeignKeyConstraint $foreignKey + * + * @return string + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + if (strlen($foreignKey->getName())) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + $sql .= 'FOREIGN KEY ('; + + if (count($foreignKey->getLocalColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + if (count($foreignKey->getForeignColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + if (strlen($foreignKey->getForeignTableName()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + $sql .= implode(', ', $foreignKey->getLocalColumns()) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getForeignColumns()) . ')'; + + return $sql; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration. + */ + public function getUniqueFieldDeclarationSQL() + { + return 'UNIQUE'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $charset name of the charset + * + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration. + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return ''; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation name of the collation + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getColumnCollationDeclarationSQL($collation) + { + return ''; + } + + /** + * Whether the platform prefers sequences for ID generation. + * Subclasses should override this method to return TRUE if they prefer sequences. + * + * @return boolean + */ + public function prefersSequences() + { + return false; + } + + /** + * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. + * Subclasses should override this method to return TRUE if they prefer identity columns. + * + * @return boolean + */ + public function prefersIdentityColumns() + { + return false; + } + + /** + * Some platforms need the boolean values to be converted. + * + * The default conversion in this implementation converts to integers (false => 0, true => 1). + * + * @param mixed $item + * + * @return mixed + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } else if (is_bool($item)) { + $item = (int) $item; + } + + return $item; + } + + /** + * Gets the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Gets the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Gets the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Get sql for transaction isolation level Connection constant + * + * @param integer $level + * + * @return string + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case Connection::TRANSACTION_REPEATABLE_READ: + return 'REPEATABLE READ'; + case Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL to list all views of a database or user. + * + * @param string $database + * + * @return string + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the list of indexes for the current database. + * + * The current database parameter is optional but will always be passed + * when using the SchemaManager API and is the database the given table is in. + * + * Attention: Some platforms only support currentDatabase when they + * are connected with that database. Cross-database information schema + * requests may be impossible. + * + * @param string $table + * @param string $currentDatabase + * + * @return string + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL snippet to drop an existing sequence + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * create a new database + * + * @param string $database name of the database that should be created + * + * @return string + */ + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get sql to set the transaction isolation level + * + * @param integer $level + * + * @return string + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create datetime with timezone offset fields. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + + /** + * Obtain DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getFloatDeclarationSQL(array $fieldDeclaration) + { + return 'DOUBLE PRECISION'; + } + + /** + * Gets the default transaction isolation level of the platform. + * + * @return integer The default isolation level. + * + * @see Doctrine\DBAL\Connection\TRANSACTION_* constants. + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_COMMITTED; + } + + /* supports*() methods */ + + /** + * Whether the platform supports sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return false; + } + + /** + * Whether the platform supports identity columns. + * Identity columns are columns that receive an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + /** + * Whether the platform supports altering tables. + * + * @return boolean + */ + public function supportsAlterTable() + { + return true; + } + + /** + * Whether the platform supports transactions. + * + * @return boolean + */ + public function supportsTransactions() + { + return true; + } + + /** + * Whether the platform supports savepoints. + * + * @return boolean + */ + public function supportsSavepoints() + { + return true; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return $this->supportsSavepoints(); + } + + /** + * Whether the platform supports primary key constraints. + * + * @return boolean + */ + public function supportsPrimaryConstraints() + { + return true; + } + + /** + * Does the platform supports foreign key constraints? + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Does this platform supports onUpdate in foreign key constraints? + * + * @return boolean + */ + public function supportsForeignKeyOnUpdate() + { + return ($this->supportsForeignKeyConstraints() && true); + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } + + /** + * Can this platform emulate schemas? + * + * Platforms that either support or emulate schemas don't automatically + * filter a schema for the namespaced elements in {@link + * AbstractManager#createSchema}. + * + * @return boolean + */ + public function canEmulateSchemas() + { + return false; + } + + /** + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return boolean + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * Whether the platform supports getting the affected rows of a recent + * update/delete type query. + * + * @return boolean + */ + public function supportsGettingAffectedRows() + { + return true; + } + + /** + * Does this platform support to add inline column comments as postfix. + * + * @return boolean + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Does this platform support the proprietary syntax "COMMENT ON asset" + * + * @return boolean + */ + public function supportsCommentOnStatement() + { + return false; + } + + /** + * Does this platform have native guid type. + * + * @return boolean + */ + public function hasNativeGuidType() + { + return false; + } + + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Does this platform views ? + * + * @return boolean + */ + public function supportsViews() + { + return true; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime value of this platform. + * + * @return string The format string. + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime with timezone value of this platform. + * + * @return string The format string. + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored date value of this platform. + * + * @return string The format string. + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored time value of this platform. + * + * @return string The format string. + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * Modify limit query + * + * @param string $query + * @param integer $limit + * @param integer $offset + * + * @return string + */ + final public function modifyLimitQuery($query, $limit, $offset = null) + { + if ($limit !== null) { + $limit = (int)$limit; + } + + if ($offset !== null) { + $offset = (int)$offset; + + if ($offset < 0) { + throw new DBALException("LIMIT argument offset=$offset is not valid"); + } + if ($offset > 0 && ! $this->supportsLimitOffset()) { + throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName())); + } + } + + return $this->doModifyLimitQuery($query, $limit, $offset); + } + + /** + * Adds an driver-specific LIMIT clause to the query + * + * @param string $query + * @param integer $limit + * @param integer $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Does the database platform support offsets in modify limit clauses? + * + * @return boolean + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * @param string $column The column name for which to get the correct character casing. + * + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return $column; + } + + /** + * Makes any fixes to a name of a schema element (table, sequence, ...) that are required + * by restrictions of the platform, like a maximum length. + * + * @param string $schemaElementName + * + * @return string + */ + public function fixSchemaElementName($schemaElementName) + { + return $schemaElementName; + } + + /** + * Maximum length of any given database identifier, like tables or column names. + * + * @return integer + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generate a Truncate Table SQL statement for a given table. + * + * Cascade is not supported on many platforms but would optionally cascade the truncate by + * following the foreign keys. + * + * @param string $tableName + * @param boolean $cascade + * + * @return string + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName; + } + + /** + * This is for test reasons, many vendors have special requirements for dummy statements. + * + * @return string + */ + public function getDummySelectSQL() + { + return 'SELECT 1'; + } + + /** + * Generate SQL to create a new savepoint + * + * @param string $savepoint + * + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to release a savepoint + * + * @param string $savepoint + * + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to rollback a savepoint + * + * @param string $savepoint + * + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Return the keyword list instance of this platform. + * + * Throws exception if no keyword list is specified. + * + * @throws DBALException + * + * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + final public function getReservedKeywordsList() + { + // Check for an existing instantiation of the keywords class. + if ($this->_keywords) { + return $this->_keywords; + } + + $class = $this->getReservedKeywordsClass(); + $keywords = new $class; + if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { + throw DBALException::notSupported(__METHOD__); + } + + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $keywords; + + return $keywords; + } + + /** + * The class name of the reserved keywords list. + * + * @return string + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php new file mode 100644 index 00000000..114cd7da --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,545 @@ +. +*/ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +class DB2Platform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'clob' => 'text', + 'decimal' => 'decimal', + 'double' => 'float', + 'real' => 'float', + 'timestamp' => 'datetime', + ); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'db2'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + + return $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class. + * + * @license New BSD License + * @param string $table + * @param string $database + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE UPPER(c.tabname) = UPPER('" . $table . "') ORDER BY c.colno"; + } + + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')"; + } + + public function getListTableForeignKeysSQL($table) + { + return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ". + "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')"; + } + + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE ".$database.";"; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDateSQL() + { + return 'VALUES CURRENT DATE'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimeSQL() + { + return 'VALUES CURRENT TIME'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimestampSQL() + { + return "VALUES CURRENT TIMESTAMP"; + } + + /** + * {@inheritDoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + return $this->getUniqueConstraintDeclarationSQL($name, $index); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $indexes = array(); + if (isset($options['indexes'])) { + $indexes = $options['indexes']; + } + $options['indexes'] = array(); + + $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); + + foreach ($indexes as $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + + $queryParts = array(); + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'RENAME ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts); + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE TO ' . $diff->newName; + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $field['default'] = 0; + } else if((string)$field['type'] == "DateTime") { + $field['default'] = "00-00-00 00:00:00"; + } else if ((string)$field['type'] == "Date") { + $field['default'] = "00-00-00"; + } else if((string)$field['type'] == "Time") { + $field['default'] = "00:00:00"; + } else { + $field['default'] = ''; + } + } + + unset($field['default']); // @todo this needs fixing + if (isset($field['version']) && $field['version']) { + if ((string)$field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + $limit = (int)$limit; + $offset = (int)(($offset)?:0); + + // Todo OVER() needs ORDER BY data! + $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '. + 'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit); + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * DB2 returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + public function getForUpdateSQL() + { + return ' WITH RR USE AND KEEP UPDATE LOCKS'; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM sysibm.sysdummy1'; + } + + /** + * {@inheritDoc} + * + * DB2 supports savepoints, but they work semantically different than on other vendor platforms. + * + * TODO: We have to investigate how to get DB2 up and running with savepoints. + */ + public function supportsSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php new file mode 100644 index 00000000..47bb364e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -0,0 +1,495 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * Drizzle platform + * + * @author Kim Hemsø Rasmussen + */ +class DrizzlePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + + /** + * {@inheritDoc} + */ public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + return $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'varchar' => 'string', + 'integer' => 'integer', + 'blob' => 'text', + 'decimal' => 'decimal', + 'datetime' => 'datetime', + 'date' => 'date', + 'time' => 'time', + 'text' => 'text', + 'timestamp' => 'datetime', + 'double' => 'float', + 'bigint' => 'bigint', + ); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + public function getListDatabasesSQL() + { + return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DrizzleKeywords'; + } + + public function getListTablesSQL() + { + return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; + } + + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT, CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT," . + " NUMERIC_PRECISION, NUMERIC_SCALE" . + " FROM DATA_DICTIONARY.COLUMNS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS, UPDATE_RULE, DELETE_RULE" . + " FROM DATA_DICTIONARY.FOREIGN_KEYS" . + " WHERE CONSTRAINT_SCHEMA=" . $database . " AND CONSTRAINT_TABLE='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT INDEX_NAME AS 'key_name', COLUMN_NAME AS 'column_name', IS_USED_IN_PRIMARY AS 'primary', IS_UNIQUE=0 AS 'non_unique'" . + " FROM DATA_DICTIONARY.INDEX_PARTS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsViews() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } else if (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // drizzle primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param Index $index + * @param Table $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->newName; + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 'true' : 'false'; + } + } + } else if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php new file mode 100644 index 00000000..77c1c674 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,438 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + public function getName() + { + return 'DB2'; + } + + protected function getKeywords() + { + return array( + 'ACTIVATE', + 'ADD', + 'AFTER', + 'ALIAS', + 'ALL', + 'ALLOCATE', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', + 'ALLOW', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASENSITIVE', + 'ASSOCIATE', + 'ASUTIME', + 'AT', + 'ATTRIBUTES', + 'AUDIT', + 'AUTHORIZATION', + 'AUX', + 'AUXILIARY', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BINARY', + 'BUFFERPOOL', + 'BY', + 'CACHE', + 'CALL', + 'CALLED', + 'CAPTURE', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CCSID', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'CLONE', + 'CLOSE', + 'CLUSTER', + 'COLLECTION', + 'COLLID', + 'COLUMN', + 'COMMENT', + 'COMMIT', + 'CONCAT', + 'CONDITION', + 'CONNECT', + 'CONNECTION', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'COUNT', + 'COUNT_BIG', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_LC_CTYPE', + 'CURRENT_PATH', + 'CURRENT_SCHEMA', + 'CURRENT_SERVER', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TIMEZONE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATA', + 'DATABASE', + 'DATAPARTITIONNAME', + 'DATAPARTITIONNUM', + 'EDITPROC', + 'ELSE', + 'ELSEIF', + 'ENABLE', + 'ENCODING', + 'ENCRYPTION', + 'END', + 'END-EXEC', + 'ENDING', + 'ERASE', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXCEPTION', + 'EXCLUDING', + 'EXCLUSIVE', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'EXTERNAL', + 'EXTRACT', + 'FENCED', + 'FETCH', + 'FIELDPROC', + 'FILE', + 'FINAL', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GENERAL', + 'GENERATED', + 'GET', + 'GLOBAL', + 'GO', + 'GOTO', + 'GRANT', + 'GRAPHIC', + 'GROUP', + 'HANDLER', + 'HASH', + 'HASHED_VALUE', + 'HAVING', + 'HINT', + 'HOLD', + 'HOUR', + 'HOURS', + 'IDENTITY', + 'IF', + 'IMMEDIATE', + 'IN', + 'INCLUDING', + 'INCLUSIVE', + 'INCREMENT', + 'INDEX', + 'INDICATOR', + 'INF', + 'INFINITY', + 'INHERIT', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INTEGRITY', + 'MATERIALIZED', + 'MAXVALUE', + 'MICROSECOND', + 'MICROSECONDS', + 'MINUTE', + 'MINUTES', + 'MINVALUE', + 'MODE', + 'MODIFIES', + 'MONTH', + 'MONTHS', + 'NAN', + 'NEW', + 'NEW_TABLE', + 'NEXTVAL', + 'NO', + 'NOCACHE', + 'NOCYCLE', + 'NODENAME', + 'NODENUMBER', + 'NOMAXVALUE', + 'NOMINVALUE', + 'NONE', + 'NOORDER', + 'NORMALIZED', + 'NOT', + 'NULL', + 'NULLS', + 'NUMPARTS', + 'OBID', + 'OF', + 'OLD', + 'OLD_TABLE', + 'ON', + 'OPEN', + 'OPTIMIZATION', + 'OPTIMIZE', + 'OPTION', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERRIDING', + 'PACKAGE', + 'PADDED', + 'PAGESIZE', + 'PARAMETER', + 'PART', + 'PARTITION', + 'PARTITIONED', + 'PARTITIONING', + 'PARTITIONS', + 'PASSWORD', + 'PATH', + 'PIECESIZE', + 'PLAN', + 'POSITION', + 'PRECISION', + 'PREPARE', + 'PREVVAL', + 'PRIMARY', + 'PRIQTY', + 'PRIVILEGES', + 'PROCEDURE', + 'PROGRAM', + 'PSID', + 'ROUND_UP', + 'ROUTINE', + 'ROW', + 'ROW_NUMBER', + 'ROWNUMBER', + 'ROWS', + 'ROWSET', + 'RRN', + 'RUN', + 'SAVEPOINT', + 'SCHEMA', + 'SCRATCHPAD', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SECONDS', + 'SECQTY', + 'SECURITY', + 'SELECT', + 'SENSITIVE', + 'SEQUENCE', + 'SESSION', + 'SESSION_USER', + 'SET', + 'SIGNAL', + 'SIMPLE', + 'SNAN', + 'SOME', + 'SOURCE', + 'SPECIFIC', + 'SQL', + 'SQLID', + 'STACKED', + 'STANDARD', + 'START', + 'STARTING', + 'STATEMENT', + 'STATIC', + 'STATMENT', + 'STAY', + 'STOGROUP', + 'STORES', + 'STYLE', + 'SUBSTRING', + 'SUMMARY', + 'SYNONYM', + 'SYSFUN', + 'SYSIBM', + 'SYSPROC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESPACE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'TRIM', + 'TRUNCATE', + 'TYPE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNTIL', + 'UPDATE', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'INTERSECT', + 'PUBLIC', + 'USAGE', + 'INTO', + 'QUERY', + 'USER', + 'IS', + 'QUERYNO', + 'USING', + 'ISOBID', + 'RANGE', + 'VALIDPROC', + 'ISOLATION', + 'RANK', + 'VALUE', + 'ITERATE', + 'READ', + 'VALUES', + 'JAR', + 'READS', + 'VARIABLE', + 'JAVA', + 'RECOVERY', + 'VARIANT', + 'JOIN', + 'REFERENCES', + 'VCAT', + 'KEEP', + 'REFERENCING', + 'VERSION', + 'KEY', + 'REFRESH', + 'VIEW', + 'LABEL', + 'RELEASE', + 'VOLATILE', + 'LANGUAGE', + 'RENAME', + 'VOLUMES', + 'LATERAL', + 'REPEAT', + 'WHEN', + 'LC_CTYPE', + 'RESET', + 'WHENEVER', + 'LEAVE', + 'RESIGNAL', + 'WHERE', + 'LEFT', + 'RESTART', + 'WHILE', + 'LIKE', + 'RESTRICT', + 'WITH', + 'LINKTYPE', + 'RESULT', + 'WITHOUT', + 'LOCAL', + 'RESULT_SET_LOCATOR WLM', + 'LOCALDATE', + 'RETURN', + 'WRITE', + 'LOCALE', + 'RETURNS', + 'XMLELEMENT', + 'LOCALTIME', + 'REVOKE', + 'XMLEXISTS', + 'LOCALTIMESTAMP RIGHT', + 'XMLNAMESPACES', + 'LOCATOR', + 'ROLE', + 'YEAR', + 'LOCATORS', + 'ROLLBACK', + 'YEARS', + ); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php new file mode 100644 index 00000000..c6d3187b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php @@ -0,0 +1,340 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Drizzle Keywordlist + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleKeywords extends KeywordList +{ + public function getName() + { + return 'drizzle'; + } + + protected function getKeywords() + { + return array( + 'ABS', + 'ALL', + 'ALLOCATE', + 'ALTER', + 'AND', + 'ANY', + 'ARE', + 'ARRAY', + 'AS', + 'ASENSITIVE', + 'ASYMMETRIC', + 'AT', + 'ATOMIC', + 'AUTHORIZATION', + 'AVG', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', + 'BOTH', + 'BY', + 'CALL', + 'CALLED', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CEIL', + 'CEILING', + 'CHAR', + 'CHARACTER', + 'CHARACTER_LENGTH', + 'CHAR_LENGTH', + 'CHECK', + 'CLOB', + 'CLOSE', + 'COALESCE', + 'COLLATE', + 'COLLECT', + 'COLUMN', + 'COMMIT', + 'CONDITION', + 'CONNECT', + 'CONSTRAINT', + 'CONVERT', + 'CORR', + 'CORRESPONDING', + 'COUNT', + 'COVAR_POP', + 'COVAR_SAMP', + 'CREATE', + 'CROSS', + 'CUBE', + 'CUME_DIST', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_DEFAULT_TRANSFORM_GROUP', + 'CURRENT_PATH', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TRANSFORM_GROUP_FOR_TYPE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATE', + 'DAY', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENSE_RANK', + 'DEREF', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISCONNECT', + 'DISTINCT', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'EACH', + 'ELEMENT', + 'ELSE', + 'END', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXP', + 'EXTERNAL', + 'EXTRACT', + 'FALSE', + 'FETCH', + 'FILTER', + 'FLOAT', + 'FLOOR', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'FUSION', + 'GET', + 'GLOBAL', + 'GRANT', + 'GROUP', + 'GROUPING', + 'HAVING', + 'HOLD', + 'HOUR', + 'IDENTITY', + 'IN', + 'INDICATOR', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INTEGER', + 'INTERSECT', + 'INTERSECTION', + 'INTERVAL', + 'INTO', + 'IS', + 'JOIN', + 'LANGUAGE', + 'LARGE', + 'LATERAL', + 'LEADING', + 'LEFT', + 'LIKE', + 'LN', + 'LOCAL', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOWER', + 'MATCH', + 'MAX', + 'MEMBER', + 'MERGE', + 'METHOD', + 'MIN', + 'MINUTE', + 'MOD', + 'MODIFIES', + 'MODULE', + 'MONTH', + 'MULTISET', + 'NATIONAL', + 'NATURAL', + 'NCHAR', + 'NCLOB', + 'NEW', + 'NO', + 'NONE', + 'NORMALIZE', + 'NOT', + 'NULL_SYM', + 'NULLIF', + 'NUMERIC', + 'OCTET_LENGTH', + 'OF', + 'OLD', + 'ON', + 'ONLY', + 'OPEN', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'OVERLAY', + 'PARAMETER', + 'PARTITION', + 'PERCENTILE_CONT', + 'PERCENTILE_DISC', + 'PERCENT_RANK', + 'POSITION', + 'POWER', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PROCEDURE', + 'RANGE', + 'RANK', + 'READS', + 'REAL', + 'RECURSIVE', + 'REF', + 'REFERENCES', + 'REFERENCING', + 'REGR_AVGX', + 'REGR_AVGY', + 'REGR_COUNT', + 'REGR_INTERCEPT', + 'REGR_R2', + 'REGR_SLOPE', + 'REGR_SXX', + 'REGR_SXY', + 'REGR_SYY', + 'RELEASE', + 'RESULT', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'ROW', + 'ROWS', + 'ROW_NUMBER', + 'SAVEPOINT', + 'SCOPE', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SELECT', + 'SENSITIVE', + 'SESSION_USER', + 'SET', + 'SIMILAR', + 'SMALLINT', + 'SOME', + 'SPECIFIC', + 'SPECIFICTYPE', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQRT', + 'START', + 'STATIC', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'SUBMULTISET', + 'SUBSTRING', + 'SUM', + 'SYMMETRIC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TIMEZONE_HOUR', + 'TIMEZONE_MINUTE', + 'TO', + 'TRAILING', + 'TRANSLATE', + 'TRANSLATION', + 'TREAT', + 'TRIGGER', + 'TRIM', + 'TRUE', + 'UESCAPE', + 'UNION', + 'UNIQUE', + 'UNKNOWN', + 'UNNEST', + 'UPDATE', + 'UPPER', + 'USER', + 'USING', + 'VALUE', + 'VALUES', + 'VARCHAR', + 'VARYING', + 'VAR_POP', + 'VAR_SAMP', + 'WHEN', + 'WHENEVER', + 'WHERE', + 'WIDTH_BUCKET', + 'WINDOW', + 'WITH', + 'WITHIN', + 'WITHOUT', + 'XML', + 'XMLAGG', + 'XMLATTRIBUTES', + 'XMLBINARY', + 'XMLCOMMENT', + 'XMLCONCAT', + 'XMLELEMENT', + 'XMLFOREST', + 'XMLNAMESPACES', + 'XMLPARSE', + 'XMLPI', + 'XMLROOT', + 'XMLSERIALIZE', + 'YEAR', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php new file mode 100644 index 00000000..f30bb368 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,63 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + private $keywords = null; + + /** + * Check if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * @return bool + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + abstract protected function getKeywords(); + + /** + * Name of this keyword list. + * + * @return string + */ + abstract public function getName(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php new file mode 100644 index 00000000..50f1c742 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,43 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MsSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + * @deprecated Use SQLServerKeywords class instead. + */ +class MsSQLKeywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MsSQL'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php new file mode 100644 index 00000000..c4ad5d68 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,269 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MySQLKeywords extends KeywordList +{ + public function getName() + { + return 'MySQL'; + } + + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONNECTION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LABEL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MATCH', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NOT', + 'NO_WRITE_TO_BINLOG', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RAID0', + 'RANGE', + 'READ', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SMALLINT', + 'SONAME', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'X509', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php new file mode 100644 index 00000000..9f34ba64 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,157 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class OracleKeywords extends KeywordList +{ + public function getName() + { + return 'Oracle'; + } + + protected function getKeywords() + { + return array( + 'ACCESS', + 'ELSE', + 'MODIFY', + 'START', + 'ADD', + 'EXCLUSIVE', + 'NOAUDIT', + 'SELECT', + 'ALL', + 'EXISTS', + 'NOCOMPRESS', + 'SESSION', + 'ALTER', + 'FILE', + 'NOT', + 'SET', + 'AND', + 'FLOAT', + 'NOTFOUND ', + 'SHARE', + 'ANY', + 'FOR', + 'NOWAIT', + 'SIZE', + 'ARRAYLEN', + 'FROM', + 'NULL', + 'SMALLINT', + 'AS', + 'GRANT', + 'NUMBER', + 'SQLBUF', + 'ASC', + 'GROUP', + 'OF', + 'SUCCESSFUL', + 'AUDIT', + 'HAVING', + 'OFFLINE ', + 'SYNONYM', + 'BETWEEN', + 'IDENTIFIED', + 'ON', + 'SYSDATE', + 'BY', + 'IMMEDIATE', + 'ONLINE', + 'TABLE', + 'CHAR', + 'IN', + 'OPTION', + 'THEN', + 'CHECK', + 'INCREMENT', + 'OR', + 'TO', + 'CLUSTER', + 'INDEX', + 'ORDER', + 'TRIGGER', + 'COLUMN', + 'INITIAL', + 'PCTFREE', + 'UID', + 'COMMENT', + 'INSERT', + 'PRIOR', + 'UNION', + 'COMPRESS', + 'INTEGER', + 'PRIVILEGES', + 'UNIQUE', + 'CONNECT', + 'INTERSECT', + 'PUBLIC', + 'UPDATE', + 'CREATE', + 'INTO', + 'RAW', + 'USER', + 'CURRENT', + 'IS', + 'RENAME', + 'VALIDATE', + 'DATE', + 'LEVEL', + 'RESOURCE', + 'VALUES', + 'DECIMAL', + 'LIKE', + 'REVOKE', + 'VARCHAR', + 'DEFAULT', + 'LOCK', + 'ROW', + 'VARCHAR2', + 'DELETE', + 'LONG', + 'ROWID', + 'VIEW', + 'DESC', + 'MAXEXTENTS', + 'ROWLABEL', + 'WHENEVER', + 'DISTINCT', + 'MINUS', + 'ROWNUM', + 'WHERE', + 'DROP', + 'MODE', + 'ROWS', + 'WITH', + 'RANGE', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 00000000..7950f6a7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,131 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + public function getName() + { + return 'PostgreSQL'; + } + + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php new file mode 100644 index 00000000..a61922bf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,116 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class ReservedKeywordsValidator implements Visitor +{ + /** + * @var KeywordList[] + */ + private $keywordLists = array(); + + /** + * @var array + */ + private $violations = array(); + + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + public function getViolations() + { + return $this->violations; + } + + /** + * @param string $word + * @return array + */ + private function isReservedWord($word) + { + if ($word[0] == "`") { + $word = str_replace('`', '', $word); + } + + $keywordLists = array(); + foreach ($this->keywordLists as $keywordList) { + if ($keywordList->isKeyword($word)) { + $keywordLists[] = $keywordList->getName(); + } + } + return $keywordLists; + } + + private function addViolation($asset, $violatedPlatforms) + { + if ( ! $violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + + } + + public function acceptIndex(Table $table, Index $index) + { + + } + + public function acceptSchema(Schema $schema) + { + + } + + public function acceptSequence(Sequence $sequence) + { + + } + + public function acceptTable(Table $table) + { + $this->addViolation( + 'Table ' . $table->getName(), + $this->isReservedWord($table->getName()) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php new file mode 100644 index 00000000..3a743491 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2005 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2005Keywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2005'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-US/library/ms189822%28v=sql.90%29.aspx + */ + protected function getKeywords() + { + return array_merge(array_diff(parent::getKeywords(), array('DUMMY')), array( + 'EXTERNAL', + 'PIVOT', + 'REVERT', + 'SECURITYAUDIT', + 'TABLESAMPLE', + 'UNPIVOT' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php new file mode 100644 index 00000000..38556b50 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2008 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2008Keywords extends SQLServer2005Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2008'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822%28v=sql.100%29.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'MERGE' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php new file mode 100644 index 00000000..ada1c026 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2012 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2012Keywords extends SQLServer2008Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2012'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'SEMANTICKEYPHRASETABLE', + 'SEMANTICSIMILARITYDETAILSTABLE', + 'SEMANTICSIMILARITYTABLE', + 'TRY_CONVERT', + 'WITHIN GROUP' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php new file mode 100644 index 00000000..fb43d2d5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2000 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + */ +class SQLServerKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BREAK', + 'BROWSE', + 'BULK', + 'BY', + 'CASCADE', + 'CASE', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'CLUSTERED', + 'COALESCE', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'COMPUTE', + 'CONSTRAINT', + 'CONTAINS', + 'CONTAINSTABLE', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DBCC', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENY', + 'DESC', + 'DISK', + 'DISTINCT', + 'DISTRIBUTED', + 'DOUBLE', + 'DROP', + 'DUMP', + 'ELSE', + 'END', + 'ERRLVL', + 'ESCAPE', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXTERNAL', + 'FETCH', + 'FILE', + 'FILLFACTOR', + 'FOR', + 'FOREIGN', + 'FREETEXT', + 'FREETEXTTABLE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTITY', + 'IDENTITY_INSERT', + 'IDENTITYCOL', + 'IF', + 'IN', + 'INDEX', + 'INNER', + 'INSERT', + 'INTERSECT', + 'INTO', + 'IS', + 'JOIN', + 'KEY', + 'KILL', + 'LEFT', + 'LIKE', + 'LINENO', + 'LOAD', + 'NATIONAL', + 'NOCHECK ', + 'NONCLUSTERED', + 'NOT', + 'NULL', + 'NULLIF', + 'OF', + 'OFF', + 'OFFSETS', + 'ON', + 'OPEN', + 'OPENDATASOURCE', + 'OPENQUERY', + 'OPENROWSET', + 'OPENXML', + 'OPTION', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'PERCENT', + 'PIVOT', + 'PLAN', + 'PRECISION', + 'PRIMARY', + 'PRINT', + 'PROC', + 'PROCEDURE', + 'PUBLIC', + 'RAISERROR', + 'READ', + 'READTEXT', + 'RECONFIGURE', + 'REFERENCES', + 'REPLICATION', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVERT', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROWCOUNT', + 'ROWGUIDCOL', + 'RULE', + 'SAVE', + 'SCHEMA', + 'SECURITYAUDIT', + 'SELECT', + 'SESSION_USER', + 'SET', + 'SETUSER', + 'SHUTDOWN', + 'SOME', + 'STATISTICS', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'TEXTSIZE', + 'THEN', + 'TO', + 'TOP', + 'TRAN', + 'TRANSACTION', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNION', + 'UNIQUE', + 'UNPIVOT', + 'UPDATE', + 'UPDATETEXT', + 'USE', + 'USER', + 'VALUES', + 'VARYING', + 'VIEW', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITETEXT' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php new file mode 100644 index 00000000..d45b9944 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,164 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + public function getName() + { + return 'SQLite'; + } + + protected function getKeywords() + { + return array( + 'ABORT', + 'ACTION', + 'ADD', + 'AFTER', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ATTACH', + 'AUTOINCREMENT', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'CONFLICT', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'DATABASE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DROP', + 'EACH', + 'ELSE', + 'END', + 'ESCAPE', + 'EXCEPT', + 'EXCLUSIVE', + 'EXISTS', + 'EXPLAIN', + 'FAIL', + 'FOR', + 'FOREIGN', + 'FROM', + 'FULL', + 'GLOB', + 'GROUP', + 'HAVING', + 'IF', + 'IGNORE', + 'IMMEDIATE', + 'IN', + 'INDEX', + 'INDEXED', + 'INITIALLY', + 'INNER', + 'INSERT', + 'INSTEAD', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'KEY', + 'LEFT', + 'LIKE', + 'LIMIT', + 'MATCH', + 'NATURAL', + 'NO', + 'NOT', + 'NOTNULL', + 'NULL', + 'OF', + 'OFFSET', + 'ON', + 'OR', + 'ORDER', + 'OUTER', + 'PLAN', + 'PRAGMA', + 'PRIMARY', + 'QUERY', + 'RAISE', + 'REFERENCES', + 'REGEXP', + 'REINDEX', + 'RELEASE', + 'RENAME', + 'REPLACE', + 'RESTRICT', + 'RIGHT', + 'ROLLBACK', + 'ROW', + 'SAVEPOINT', + 'SELECT', + 'SET', + 'TABLE', + 'TEMP', + 'TEMPORARY', + 'THEN', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USING', + 'VACUUM', + 'VALUES', + 'VIEW', + 'VIRTUAL', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php new file mode 100644 index 00000000..e67b3bf0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,821 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * The MySqlPlatform provides the behavior, features and SQL dialect of the + * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that + * uses the InnoDB storage engine. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: MySQLPlatform + */ +class MySqlPlatform extends AbstractPlatform +{ + const LENGTH_LIMIT_TINYTEXT = 255; + const LENGTH_LIMIT_TEXT = 65535; + const LENGTH_LIMIT_MEDIUMTEXT = 16777215; + + const LENGTH_LIMIT_TINYBLOB = 255; + const LENGTH_LIMIT_BLOB = 65535; + const LENGTH_LIMIT_MEDIUMBLOB = 16777215; + + /** + * Adds MySQL-specific LIMIT clause to the query + * 18446744073709551615 is 2^64-1 maximum of unsigned BIGINT the biggest limit possible + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + } elseif ($offset !== null) { + $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * Two approaches to listing the table indexes. The information_schema is + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + if ($currentDatabase) { + return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ". + "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ". + "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " . + "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " . + "FROM information_schema.STATISTICS WHERE TABLE_NAME = '" . $table . "' AND TABLE_SCHEMA = '" . $currentDatabase . "'"; + } + + return 'SHOW INDEX FROM ' . $table; + } + + public function getListViewsSQL($database) + { + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'"; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ". + "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ". + "FROM information_schema.key_column_usage k /*!50116 ". + "INNER JOIN information_schema.referential_constraints c ON ". + " c.constraint_name = k.constraint_name AND ". + " c.table_name = '$table' */ WHERE k.table_name = '$table'"; + + if ($database) { + $sql .= " AND k.table_schema = '$database' /*!50116 AND c.constraint_schema = '$database' */"; + } + + $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL"; + + return $sql; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'TINYINT(1)'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation name of the collation + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getCollationFieldDeclaration($collation) + { + return 'COLLATE ' . $collation; + } + + /** + * {@inheritDoc} + * + * MySql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MySql supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ". + "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " . + "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS CollactionName ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '" . $database . "' AND TABLE_NAME = '" . $table . "'"; + } + + return 'DESCRIBE ' . $table; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Charset + if ( ! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = '%s' ", str_replace("'", "''", $comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->newName; + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->name; + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $columns = $addIndex->getColumns(); + $type = ''; + if ($addIndex->isUnique()) { + $type = 'UNIQUE '; + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $type . 'INDEX ' . $addIndex->getName(); + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } else if ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + + return $unsigned . $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } else if(is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // mysql primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigint' => 'bigint', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'string' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'longblob' => 'blob', + 'blob' => 'blob', + 'mediumblob' => 'blob', + 'tinyblob' => 'blob', + 'binary' => 'blob', + 'varbinary' => 'blob', + 'set' => 'simple_array', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords'; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getBlobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + return 'LONGBLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php new file mode 100644 index 00000000..2eec164c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,836 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; + +/** + * OraclePlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + */ +class OraclePlatform extends AbstractPlatform +{ + /** + * Assertion for Oracle identifiers + * + * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm + * + * @param string + * + * @throws DBALException + */ + static public function assertValidIdentifier($identifier) + { + if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { + throw new DBALException("Invalid Oracle identifier"); + } + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return "SUBSTR($value, $position, $length)"; + } + + return "SUBSTR($value, $position)"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'date': + case 'time': + case 'timestamp': + default: + return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'INSTR('.$str.', '.$substr.')'; + } + + return 'INSTR('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'SYS_GUID()'; + } + + /** + * {@inheritDoc} + * + * Note: Since Oracle timestamp differences are calculated down to the microsecond we have to truncate + * them to the difference in days. This is obviously a restriction of the original functionality, but we + * need to make this a portable function. + */ + public function getDateDiffExpression($date1, $date2) + { + return "TRUNC(TO_NUMBER(SUBSTR((" . $date1 . "-" . $date2 . "), 1, INSTR(" . $date1 . "-" . $date2 .", ' '))))"; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return '(' . $date . '+' . $days . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return '(' . $date . '-' . $days . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", " . $months . ")"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", -" . $months . ")"; + } + + /** + * {@inheritDoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND('.$value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . '-' . + $this->getBitAndComparisonExpression($value1, $value2) + . '+' . $value2 . ')'; + } + + /** + * {@inheritDoc} + * + * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. + * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection + * in {@see listSequences()} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'NUMBER(1)'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'NUMBER(10)'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(20)'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(5)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + public function getListSequencesSQL($database) + { + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = '".strtoupper($database)."'"; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($table, array $columns, array $options = array()) + { + $indexes = isset($options['indexes']) ? $options['indexes'] : array(); + $options['indexes'] = array(); + $sql = parent::_getCreateTableSQL($table, $columns, $options); + + foreach ($columns as $name => $column) { + if (isset($column['sequence'])) { + $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1); + } + + if (isset($column['autoincrement']) && $column['autoincrement'] || + (isset($column['autoinc']) && $column['autoinc'])) { + $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table)); + } + } + + if (isset($indexes) && ! empty($indexes)) { + foreach ($indexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $table); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = strtoupper($table); + + return "SELECT uind.index_name AS name, " . + " uind.index_type AS type, " . + " decode( uind.uniqueness, 'NONUNIQUE', 0, 'UNIQUE', 1 ) AS is_unique, " . + " uind_col.column_name AS column_name, " . + " uind_col.column_position AS column_pos, " . + " (SELECT ucon.constraint_type FROM user_constraints ucon WHERE ucon.constraint_name = uind.index_name) AS is_primary ". + "FROM user_indexes uind, user_ind_columns uind_col " . + "WHERE uind.index_name = uind_col.index_name AND uind_col.table_name = '$table' ORDER BY uind_col.column_position ASC"; + } + + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $table = strtoupper($table); + $name = strtoupper($name); + + $sql = array(); + + $indexName = $table . '_AI_PK'; + + $idx = new Index($indexName, array($name), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \''.$table.'\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $table).'\'; + END IF; +END;'; + + $sequenceName = $table . '_' . $name . '_SEQ'; + $sequence = new Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $triggerName = $table . '_AI_PK'; + $sql[] = 'CREATE TRIGGER ' . $triggerName . ' + BEFORE INSERT + ON ' . $table . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + IF (:NEW.' . $name . ' IS NULL OR :NEW.'.$name.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequenceName . '\'; + SELECT :NEW.' . $name . ' INTO last_InsertID FROM DUAL; + WHILE (last_InsertID > last_Sequence) LOOP + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; + END LOOP; + END IF; +END;'; + + return $sql; + } + + public function getDropAutoincrementSql($table) + { + $table = strtoupper($table); + $trigger = $table . '_AI_PK'; + + $sql[] = 'DROP TRIGGER ' . $trigger; + $sql[] = $this->getDropSequenceSQL($table.'_SEQ'); + + $indexName = $table . '_AI_PK'; + $sql[] = $this->getDropConstraintSQL($indexName, $table); + + return $sql; + } + + public function getListTableForeignKeysSQL($table) + { + $table = strtoupper($table); + + return "SELECT alc.constraint_name, + alc.DELETE_RULE, + alc.search_condition, + cols.column_name \"local_column\", + cols.position, + r_alc.table_name \"references_table\", + r_cols.column_name \"foreign_column\" + FROM user_cons_columns cols +LEFT JOIN user_constraints alc + ON alc.constraint_name = cols.constraint_name +LEFT JOIN user_constraints r_alc + ON alc.r_constraint_name = r_alc.constraint_name +LEFT JOIN user_cons_columns r_cols + ON r_alc.constraint_name = r_cols.constraint_name + AND cols.position = r_cols.position + WHERE alc.constraint_name = cols.constraint_name + AND alc.constraint_type = 'R' + AND alc.table_name = '".$table."'"; + } + + public function getListTableConstraintsSQL($table) + { + $table = strtoupper($table); + return 'SELECT * FROM user_constraints WHERE table_name = \'' . $table . '\''; + } + + public function getListTableColumnsSQL($table, $database = null) + { + $table = strtoupper($table); + + $tabColumnsTableName = "user_tab_columns"; + $ownerCondition = ''; + + if (null !== $database){ + $database = strtoupper($database); + $tabColumnsTableName = "all_tab_columns"; + $ownerCondition = "AND c.owner = '".$database."'"; + } + + return "SELECT c.*, d.comments FROM $tabColumnsTableName c ". + "INNER JOIN user_col_comments d ON d.TABLE_NAME = c.TABLE_NAME AND d.COLUMN_NAME = c.COLUMN_NAME ". + "WHERE c.table_name = '" . $table . "' ".$ownerCondition." ORDER BY c.column_name"; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return 'DROP USER ' . $database . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + $fields = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $columnHasChangedComment = $columnDiff->hasChanged('comment'); + + /** + * Do not add query part if only comment has changed + */ + if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { + $fields[] = $column->getQuotedName($this). ' ' . $this->getColumnDeclarationSQL('', $column->toArray()); + } + + if ($columnHasChangedComment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->name, + $column->getName(), + $this->getColumnComment($column) + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName .' TO ' . $column->getQuotedName($this); + } + + $fields = array(); + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $column->getQuotedName($this); + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'oracle'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + $limit = (int) $limit; + $offset = (int) $offset; + + if (preg_match('/^\s*SELECT/i', $query)) { + if (!preg_match('/\sFROM\s/i', $query)) { + $query .= " FROM dual"; + } + if ($limit > 0) { + $max = $offset + $limit; + $column = '*'; + if ($offset > 0) { + $min = $offset + 1; + $query = 'SELECT * FROM (SELECT a.' . $column . ', rownum AS doctrine_rownum FROM (' . + $query . + ') a WHERE rownum <= ' . $max . ') WHERE doctrine_rownum >= ' . $min; + } else { + $query = 'SELECT a.' . $column . ' FROM (' . $query . ') a WHERE ROWNUM <= ' . $max; + } + } + } + + return $query; + } + + /** + * {@inheritDoc} + * + * Oracle returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sP'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d 00:00:00'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return '1900-01-01 H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function fixSchemaElementName($schemaElementName) + { + if (strlen($schemaElementName) > 30) { + // Trim it + return substr($schemaElementName, 0, 30); + } + + return $schemaElementName; + } + + /** + * {@inheritDoc} + */ + public function getMaxIdentifierLength() + { + return 30; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyOnUpdate() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'integer' => 'integer', + 'number' => 'integer', + 'pls_integer' => 'boolean', + 'binary_integer' => 'boolean', + 'varchar' => 'string', + 'varchar2' => 'string', + 'nvarchar2' => 'string', + 'char' => 'string', + 'nchar' => 'string', + 'date' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'float' => 'float', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'text', + 'long raw' => 'text', + 'rowid' => 'string', + 'urowid' => 'string', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php new file mode 100644 index 00000000..048717eb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,822 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Table; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * @var bool + */ + private $useBooleanTrueFalseStrings = true; + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool)$flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + + return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END'; + } + + return 'POSITION('.$substr.' IN '.$str.')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return "(" . $date ." + (" . $days . " || ' day')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return "(" . $date ." - (" . $days . " || ' day')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "(" . $date ." + (" . $months . " || ' month')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "(" . $date ." - (" . $months . " || ' month')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + public function getListSequencesSQL($database) + { + return "SELECT + c.relname, n.nspname AS schemaname + FROM + pg_class c, pg_namespace n + WHERE relkind = 'S' AND n.oid = c.relnamespace AND + (n.nspname NOT LIKE 'pg_%' AND n.nspname != 'information_schema')"; + } + + public function getListTablesSQL() + { + return "SELECT tablename AS table_name, schemaname AS schema_name + FROM pg_tables WHERE schemaname NOT LIKE 'pg_%' AND schemaname != 'information_schema' AND tablename != 'geometry_columns' AND tablename != 'spatial_ref_sys'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT viewname, definition FROM pg_views'; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE " .$this->getTableWhereClause($table) ." AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getListTableConstraintsSQL($table) + { + return "SELECT + relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = '$table' + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + )"; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid"; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + * + * @return string + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') + { + $whereClause = $namespaceAlias.".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = "'" . $schema . "'"; + } else { + $schema = "ANY(string_to_array((select replace(setting,'\"\$user\"',user) from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + $whereClause .= "$classAlias.relname = '" . $table . "' AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + a.attname AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM + pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_attrdef.adsrc + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n + WHERE ".$this->getTableWhereClause($table, 'c', 'n') ." + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + + if (($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $oldColumnName = $columnDiff->oldColumnName; + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type')) { + $type = $column->getType(); + + // here was a server version check before, but DBAL API does not support this anymore. + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('default')) { + $query = 'ALTER ' . $oldColumnName . ' SET ' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $diff->name . '_' . $oldColumnName . '_seq'; + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->name . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } else { + // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have + $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT"; + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->name, + $column->getName(), + $this->getColumnComment($column) + ); + } + + if ($columnDiff->hasChanged('length')) { + $query = 'ALTER ' . $column->getName() . ' TYPE ' . $column->getType()->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $comment = $comment === null ? 'NULL' : "'$comment'"; + + return "COMMENT ON COLUMN $tableName.$columnName IS $comment"; + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue(); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) { + $sequence = $sequence->getQuotedName($this); + } + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 'true' : 'false'; + } + } + } else { + if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + } + + return $item; + } + + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXTVAL('" . $sequenceName . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID_GENERATE_V4()'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'postgresql'; + } + + /** + * {@inheritDoc} + * + * PostgreSQL returns all column names in SQL result sets in lowercase. + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName.' '.(($cascade)?'CASCADE':''); + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'int2' => 'smallint', + 'serial' => 'integer', + 'serial4' => 'integer', + 'int' => 'integer', + 'int4' => 'integer', + 'integer' => 'integer', + 'bigserial' => 'bigint', + 'serial8' => 'bigint', + 'bigint' => 'bigint', + 'int8' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'text' => 'text', + 'varchar' => 'string', + 'interval' => 'string', + '_varchar' => 'string', + 'char' => 'string', + 'bpchar' => 'string', + 'inet' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'time' => 'time', + 'timetz' => 'time', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'money' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'uuid' => 'guid', + 'bytea' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BYTEA'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php new file mode 100644 index 00000000..238e54f4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Table; + +/** + * Platform to ensure compatibility of Doctrine with SQL Azure + * + * On top of SQL Server 2008 the following functionality is added: + * + * - Create tables with the FEDERATED ON syntax. + */ +class SQLAzurePlatform extends SQLServer2008Platform +{ + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) + { + $sql = parent::getCreateTableSQL($table, $createFlags); + + if ($table->hasOption('azure.federatedOnColumnName')) { + $distributionName = $table->getOption('azure.federatedOnDistributionName'); + $columnName = $table->getOption('azure.federatedOnColumnName'); + $stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')'; + + $sql[0] = $sql[0] . $stmt; + } + + return $sql; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php new file mode 100644 index 00000000..33c26ca5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2005 version and + * higher. + * + * Differences to SQL Server 2008 are: + * + * - DATETIME2 datatype does not exist, only DATETIME which has a precision of + * 3. This is not supported by PHP DateTime, so we are emulating it by + * setting .000 manually. + * - Starting with SQLServer2005 VARCHAR(MAX), VARBINARY(MAX) and + * NVARCHAR(max) replace the old TEXT, NTEXT and IMAGE types. See + * {@link http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx} + * for more information. + */ +class SQLServer2005Platform extends SQLServerPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2005 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords'; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php new file mode 100644 index 00000000..8946854c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2008 version. + * + * Differences to SQL Server 2005 and before are that a new DATETIME2 type was + * introduced that has a higher precision. + */ +class SQLServer2008Platform extends SQLServer2005Platform +{ + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + * + * Adding Datetime2 Type + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['datetime2'] = 'datetime'; + $this->doctrineTypeMapping['date'] = 'date'; + $this->doctrineTypeMapping['time'] = 'time'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2008 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php new file mode 100644 index 00000000..d5c95705 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Sequence; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2012 version. + * + * Differences to SQL Server 2008 and before are that sequences are introduced. + * + * @author Steve Müller + */ +class SQLServer2012Platform extends SQLServer2008Platform +{ + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, seq.increment, seq.start_value FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT NEXT VALUE FOR ' . $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2012 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php new file mode 100644 index 00000000..dbe9c76c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,1150 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; + +/** + * The SQLServerPlatform provides the behavior, features and SQL dialect of the + * Microsoft SQL Server database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @author Steve Müller + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return 'DATEADD(day, ' . $days . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return 'DATEADD(day, -1 * ' . $days . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return 'DATEADD(month, ' . $months . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return 'DATEADD(month, -1 * ' . $months . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } else if (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if (!isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index') + ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . " + ELSE + DROP INDEX " . $index . " ON " . $table; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $defaultConstraintsSql = array(); + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (isset($column['primary']) && $column['primary']) { + $column['notnull'] = true; + } + + /** + * Build default constraints SQL statements + */ + if ( ! empty($column['default']) || is_numeric($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $tableName . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $column); + } + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && !empty($options['primary'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (!empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && !empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return array_merge($sql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + $flags = ''; + if ($index->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param array $column Column definition. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if (empty($column['default']) && ! is_numeric($column['default'])) { + throw new \InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + return + ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $column['name']; + } + + /** + * {@inheritDoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); + + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + + return $constraint; + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && !$index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } else if ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param Index $index + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + foreach ($index->getColumns() as $field => $definition) { + if (!is_array($definition)) { + $field = $definition; + } + + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if ( ! empty($columnDef['default']) || is_numeric($columnDef['default'])) { + $columnDef['name'] = $column->getQuotedName($this); + $queryParts[] = 'ADD' . $this->getDefaultConstraintDeclarationSQL($diff->name, $columnDef); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $fromColumn = $columnDiff->fromColumn; + $fromColumnDefault = isset($fromColumn) ? $fromColumn->getDefault() : null; + $column = $columnDiff->column; + $columnDef = $column->toArray(); + $columnDefaultHasChanged = $columnDiff->hasChanged('default'); + + /** + * Drop existing column default constraint + * if default value has changed and another + * default constraint already exists for the column. + */ + if ($columnDefaultHasChanged && ( ! empty($fromColumnDefault) || is_numeric($fromColumnDefault))) { + $queryParts[] = 'DROP CONSTRAINT ' . + $this->generateDefaultConstraintName($diff->name, $columnDiff->oldColumnName); + } + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if ($columnDefaultHasChanged && (! empty($columnDef['default']) || is_numeric($columnDef['default']))) { + $columnDef['name'] = $column->getQuotedName($this); + $queryParts[] = 'ADD' . $this->getDefaultConstraintDeclarationSQL($diff->name, $columnDef); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = "sp_RENAME '". $diff->name. ".". $oldColumnName . "' , '".$column->getQuotedName($this)."', 'COLUMN'"; + + $columnDef = $column->toArray(); + + /** + * Drop existing default constraint for the old column name + * if column has default value. + */ + if ( ! empty($columnDef['default']) || is_numeric($columnDef['default'])) { + $queryParts[] = 'DROP CONSTRAINT ' . + $this->generateDefaultConstraintName($diff->name, $oldColumnName); + } + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + /** + * Readd default constraint for the new column name. + */ + if ( ! empty($columnDef['default']) || is_numeric($columnDef['default'])) { + $columnDef['name'] = $column->getQuotedName($this); + $queryParts[] = 'ADD' . $this->getDefaultConstraintDeclarationSQL($diff->name, $columnDef); + } + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = "sp_RENAME '" . $diff->name . "', '" . $diff->newName . "'"; + + /** + * Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. + */ + $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . + "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . + "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . + "'" . $this->generateIdentifierName($diff->newName) . "') + ''', ''OBJECT'';' " . + "FROM sys.default_constraints dc " . + "JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " . + "WHERE tbl.name = '" . $diff->newName . "';" . + "EXEC sp_executesql @sql"; + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + WHERE obj.type = 'U' + AND obj.name = '$table'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id + WHERE OBJECT_NAME (f.parent_object_id) = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id + WHERE tbl.name = '$table' + ORDER BY idx.index_id ASC, idxcol.index_column_id ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + /** Original query used to get those expressions + declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x'; + declare @pat varchar(10) = '%[^' + @trim_char + ']%'; + select @c as string + , @trim_char as trim_char + , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading + , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing + , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both; + */ + $pattern = "'%[^' + $char + ']%'"; + + if ($pos == self::TRIM_LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } + + if ($pos == self::TRIM_TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return '(' . implode(' + ', $args) . ')'; + } + + public function getListDatabasesSQL() + { + return 'SELECT * FROM SYS.DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (!is_null($length)) { + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return (!empty($columnDef['autoincrement'])) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit > 0) { + $orderby = stristr($query, 'ORDER BY'); + //Remove ORDER BY from $query + $query = preg_replace('/\s+ORDER\s+BY\s+([^\)]*)/', '', $query); + $over = 'ORDER BY'; + + if ( ! $orderby) { + $over .= ' (SELECT 0)'; + } else { + //Clear ORDER BY + $orderby = preg_replace('/ORDER\s+BY\s+([^\)]*)(.*)/', '$1', $orderby); + $orderbyParts = explode(',', $orderby); + $orderbyColumns = array(); + + //Split ORDER BY into parts + foreach ($orderbyParts as &$part) { + $part = trim($part); + if (preg_match('/(([^\s]*)\.)?([^\.\s]*)\s*(ASC|DESC)?/i', $part, $matches)) { + $orderbyColumns[] = array( + 'table' => empty($matches[2]) ? '[^\.\s]*' : $matches[2], + 'column' => $matches[3], + 'sort' => isset($matches[4]) ? $matches[4] : null + ); + } + } + + //Find alias for each colum used in ORDER BY + if (count($orderbyColumns)) { + foreach ($orderbyColumns as $column) { + if (preg_match('/' . $column['table'] . '\.(' . $column['column'] . ')\s*(AS)?\s*([^,\s\)]*)/i', $query, $matches)) { + $over .= ' ' . $matches[3]; + $over .= isset($column['sort']) ? ' ' . $column['sort'] . ',' : ','; + } else { + $over .= ' ' . $column['column']; + $over .= isset($column['sort']) ? ' ' . $column['sort'] . ',' : ','; + } + } + + $over = rtrim($over, ','); + } + } + + //Replace only first occurrence of FROM with $over to prevent changing FROM also in subqueries. + $query = preg_replace('/\sFROM\s/i', ", ROW_NUMBER() OVER ($over) AS doctrine_rownum FROM ", $query, 1); + + $start = $offset + 1; + $end = $offset + $limit; + + $query = "SELECT * FROM ($query) AS doctrine_tbl WHERE doctrine_rownum BETWEEN $start AND $end"; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 1 : 0; + } + } + } else if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 1 : 0; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'bigint' => 'bigint', + 'numeric' => 'decimal', + 'bit' => 'boolean', + 'smallint' => 'smallint', + 'decimal' => 'decimal', + 'smallmoney' => 'integer', + 'int' => 'integer', + 'tinyint' => 'smallint', + 'money' => 'integer', + 'float' => 'float', + 'real' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'datetimeoffset' => 'datetimetz', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'text', + 'varbinary' => 'blob', + 'image' => 'text', + 'uniqueidentifier' => 'guid', + ); + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch ($lockMode) { + case LockMode::NONE: + $lockClause = ' WITH (NOLOCK)'; + break; + case LockMode::PESSIMISTIC_READ: + $lockClause = ' WITH (HOLDLOCK, ROWLOCK)'; + break; + case LockMode::PESSIMISTIC_WRITE: + $lockClause = ' WITH (UPDLOCK, ROWLOCK)'; + break; + default: + $lockClause = ''; + } + + return $fromClause . $lockClause; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords'; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return "[" . str_replace("]", "][", $str) . "]"; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! isset($field['default'])) { + return empty($field['notnull']) ? ' NULL' : ''; + } + + if ( ! isset($field['type'])) { + return " DEFAULT '" . $field['default'] . "'"; + } + + if (in_array((string) $field['type'], array('Integer', 'BigInteger', 'SmallInteger'))) { + return " DEFAULT " . $field['default']; + } + + if ((string) $field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if ((string) $field['type'] == 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + + return " DEFAULT '" . $field['default'] . "'"; + } + + /** + * {@inheritdoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $collation; + } + + /** + * {@inheritdoc} + * + * Modifies column declaration order as it differs in Microsoft SQL Server. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $collation = (isset($field['collate']) && $field['collate']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collate']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + * + * @return string + */ + private function generateDefaultConstraintName($table, $column) + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + * + * @return string + */ + private function generateIdentifierName($identifier) + { + return strtoupper(dechex(crc32($identifier))); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php new file mode 100644 index 00000000..3f87ee13 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,987 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Constraint; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Martin Hasoň + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " + . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'time': + return 'time(\'now\')'; + case 'date': + return 'date(\'now\')'; + case 'timestamp': + default: + return 'datetime(\'now\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimChar = ($char != false) ? (', ' . $char) : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + $trimFn = 'TRIM'; + } + + return $trimFn . '(' . $str . $trimChar . ')'; + } + + /** + * {@inheritDoc} + * + * SQLite only supports the 2 parameter variant of this function + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE('.$str.', '.$substr.')'; + } + + return 'LOCATE('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + /** + * {@inheritDoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return "DATE(" . $date . ",'+". $days . " day')"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return "DATE(" . $date . ",'-". $days . " day')"; + } + + /** + * {@inheritDoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "DATE(" . $date . ",'+". $months . " month')"; + } + + /** + * {@inheritDoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "DATE(" . $date . ",'-". $months . " month')"; + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 1; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER'; + } + + /** + * {@inheritDoc} + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( + $foreignKey->getLocalColumns(), + str_replace('.', '__', $foreignKey->getForeignTableName()), + $foreignKey->getForeignColumns(), + $foreignKey->getName(), + $foreignKey->getOptions() + )); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace('.', '__', $name); + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $foreignKey) { + $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey); + } + } + + $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['alter']) && true === $options['alter']) { + return $query; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $index => $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + return $query; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = str_replace('.', '__', $table); + + return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = '$table' AND sql NOT NULL ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA table_info($table)"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA index_list($table)"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type = 'table' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE'; + $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE'); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'sqlite'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableName = str_replace('.', '__', $tableName); + + return 'DELETE FROM '.$tableName; + } + + /** + * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction() + * + * @param int|float $value + * + * @return float + */ + static public function udfSqrt($value) + { + return sqrt($value); + } + + /** + * User-defined function for Sqlite that implements MOD(a, b) + * + * @param integer $a + * @param integer $b + * + * @return integer + */ + static public function udfMod($a, $b) + { + return ($a % $b); + } + + /** + * @param string $str + * @param string $substr + * @param integer $offset + * + * @return integer + */ + static public function udfLocate($str, $substr, $offset = 0) + { + $pos = strpos($str, $substr, $offset); + if ($pos !== false) { + return $pos+1; + } + + return 0; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSql() + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'serial' => 'integer', + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'clob' => 'text', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'longvarchar' => 'string', + 'varchar2' => 'string', + 'nvarchar' => 'string', + 'image' => 'string', + 'ntext' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'blob' => 'blob', + 'integer unsigned' => 'integer', + ); + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords'; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + foreach ($diff->fromTable->getIndexes() as $index) { + if ( ! $index->isPrimary()) { + $sql[] = $this->getDropIndexSQL($index, $diff->name); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + $tableName = $diff->newName ?: $diff->name; + foreach ($this->getIndexesInAlteredTable($diff) as $indexName => $index) { + if ($index->isPrimary()) { + continue; + } + + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + $tableName = str_replace('.', '__', $tableName); + + return $tableName; + } + + /** + * {@inheritDoc} + * + * Sqlite Platform emulates schema by underscoring each dot and generating tables + * into the default database. + * + * This hack is implemented to be able to use SQLite as testdriver when + * using schema supporting databases. + */ + public function canEmulateSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + throw new DBALException('Sqlite platform does not support alter primary key.'); + } + + /** + * {@inheritdoc} + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritdoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + throw new DBALException('Sqlite platform does not support alter constraint.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags = null) + { + $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags; + + return parent::getCreateTableSQL($table, $createFlags); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA foreign_key_list($table)"; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = $this->getSimpleAlterTableSQL($diff); + if (false !== $sql) { + return $sql; + } + + $fromTable = $diff->fromTable; + if ( ! $fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $table = clone $fromTable; + + $columns = array(); + $oldColumnNames = array(); + $newColumnNames = array(); + $columnSql = array(); + + foreach ($table->getColumns() as $columnName => $column) { + $columnName = strtolower($columnName); + $columns[$columnName] = $column; + $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); + } + + foreach ($diff->removedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + unset($oldColumnNames[$columnName]); + unset($newColumnNames[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = strtolower($oldColumnName); + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($column->getName())] = $column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $column->getQuotedName($this); + } + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this); + } + } + + foreach ($diff->addedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columns[strtolower($columnName)] = $column; + } + + $sql = array(); + $tableSql = array(); + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $newTableName = $diff->newName ?: $diff->name; + + $dataTable = new Table('__temp__'.$table->getName()); + + $newTable = new Table($table->getName(), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions()); + $newTable->addOption('alter', true); + + $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); + //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0)); + $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($fromTable); + + $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); + $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($dataTable); + + if ($diff->newName && $diff->newName != $diff->name) { + $renamedTable = new Table($diff->newName); + $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this); + } + + $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + private function getSimpleAlterTableSQL(TableDiff $diff) + { + if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes) + || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes) + || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes) + ) { + return false; + } + + $table = new Table($diff->name); + + $sql = array(); + $tableSql = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $field = array_merge(array('unique' => null, 'autoincrement' => null, 'default' => null), $column->toArray()); + $type = (string) $field['type']; + switch (true) { + case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']: + case $type == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL(): + case $type == 'Date' && $field['default'] == $this->getCurrentDateSQL(): + case $type == 'Time' && $field['default'] == $this->getCurrentTimeSQL(): + return false; + } + + $field['name'] = $column->getQuotedName($this); + if (strtolower($field['type']) == 'string' && $field['length'] === null) { + $field['length'] = 255; + } + + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $newTable = new Table($diff->newName); + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this); + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + private function getColumnNamesInAlteredTable(TableDiff $diff) + { + $columns = array(); + + foreach ($diff->fromTable->getColumns() as $columnName => $column) { + $columns[strtolower($columnName)] = $column->getName(); + } + + foreach ($diff->removedColumns as $columnName => $column) { + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + $columnName = $column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + $columnName = $columnDiff->column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->addedColumns as $columnName => $column) { + $columns[strtolower($columnName)] = $columnName; + } + + return $columns; + } + + private function getIndexesInAlteredTable(TableDiff $diff) + { + $indexes = $diff->fromTable->getIndexes(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($indexes as $key => $index) { + $changed = false; + $indexColumns = array(); + foreach ($index->getColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($indexes[$key]); + continue 2; + } else { + $indexColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags()); + } + } + + foreach ($diff->removedIndexes as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName) && isset($indexes[$indexName])) { + unset($indexes[$indexName]); + } + } + + foreach (array_merge($diff->changedIndexes, $diff->addedIndexes) as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName)) { + $indexes[$indexName] = $index; + } else { + $indexes[] = $index; + } + } + + return $indexes; + } + + private function getForeignKeysInAlteredTable(TableDiff $diff) + { + $foreignKeys = $diff->fromTable->getForeignKeys(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($foreignKeys as $key => $constraint) { + $changed = false; + $localColumns = array(); + foreach ($constraint->getLocalColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($foreignKeys[$key]); + continue 2; + } else { + $localColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions()); + } + } + + foreach ($diff->removedForeignKeys as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName) && isset($foreignKeys[$constraintName])) { + unset($foreignKeys[$constraintName]); + } + } + + foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName)) { + $foreignKeys[$constraintName] = $constraint; + } else { + $foreignKeys[] = $constraint; + } + } + + return $foreignKeys; + } + + private function getPrimaryIndexInAlteredTable(TableDiff $diff) + { + $primaryIndex = array(); + + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + $primaryIndex = array($index->getName() => $index); + } + } + + return $primaryIndex; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php new file mode 100644 index 00000000..410fa825 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,119 @@ +. + */ + + +namespace Doctrine\DBAL\Portability; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +class Connection extends \Doctrine\DBAL\Connection +{ + const PORTABILITY_ALL = 255; + const PORTABILITY_NONE = 0; + const PORTABILITY_RTRIM = 1; + const PORTABILITY_EMPTY_TO_NULL = 4; + const PORTABILITY_FIX_CASE = 8; + + const PORTABILITY_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + const PORTABILITY_DRIZZLE = 13; + const PORTABILITY_SQLSRV = 13; + + /** + * @var int + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var int + */ + private $case; + + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->_platform->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } else if ($this->_platform->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } else if ($this->_platform->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } else if ($this->_platform->getName() === "drizzle") { + $params['portability'] = self::PORTABILITY_DRIZZLE; + } else if ($this->_platform->getName() === 'sqlsrv') { + $params['portability'] = $params['portabililty'] & self::PORTABILITY_SQLSRV; + } else { + $params['portability'] = $params['portability'] & self::PORTABILITY_OTHERVENDORS; + } + $this->portability = $params['portability']; + } + if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { + if ($this->_conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // make use of c-level support for case handling + $this->_conn->setAttribute(\PDO::ATTR_CASE, $params['fetch_case']); + } else { + $this->case = ($params['fetch_case'] == \PDO::CASE_LOWER) ? CASE_LOWER : CASE_UPPER; + } + } + } + return $ret; + } + + public function getPortability() + { + return $this->portability; + } + + public function getFetchCase() + { + return $this->case; + } + + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + return new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + return new Statement(parent::prepare($statement), $this); + } + + public function query() + { + $this->connect(); + + $stmt = call_user_func_array(array($this->_conn, 'query'), func_get_args()); + return new Statement($stmt, $this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php new file mode 100644 index 00000000..98076bda --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,195 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability Wrapper for a Statement + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + + /** + * @var int + */ + private $portability; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var int + */ + private $case; + + /** + * @var int + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures + * + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + public function bindParam($column, &$variable, $type = null,$length = null) + { + return $this->stmt->bindParam($column, $variable, $type); + } + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + public function columnCount() + { + return $this->stmt->columnCount(); + } + + public function errorCode() + { + return $this->stmt->errorCode(); + } + + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + } + + public function getIterator() + { + $data = $this->fetchAll(); + return new \ArrayIterator($data); + } + + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + $row = $this->stmt->fetch($fetchMode); + + $row = $this->fixRow($row, + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM), + !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE) + ); + + return $row; + } + + public function fetchAll($fetchMode = null, $columnIndex = 0) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($columnIndex != 0) { + $rows = $this->stmt->fetchAll($fetchMode, $columnIndex); + } else { + $rows = $this->stmt->fetchAll($fetchMode); + } + + $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); + $fixCase = !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE); + if ( ! $iterateRow && !$fixCase) { + return $rows; + } + + foreach ($rows as $num => $row) { + $rows[$num] = $this->fixRow($row, $iterateRow, $fixCase); + } + + return $rows; + } + + protected function fixRow($row, $iterateRow, $fixCase) + { + if ( ! $row) { + return $row; + } + + if ($fixCase) { + $row = array_change_key_case($row, $this->case); + } + + if ($iterateRow) { + foreach ($row as $k => $v) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $v === '') { + $row[$k] = null; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + return $row; + } + + public function fetchColumn($columnIndex = 0) + { + $value = $this->stmt->fetchColumn($columnIndex); + + if ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $value === '') { + $value = null; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + public function rowCount() + { + return $this->stmt->rowCount(); + } + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php new file mode 100644 index 00000000..5d55b22c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class CompositeExpression implements \Countable +{ + /** + * Constant that represents an AND composite expression + */ + const TYPE_AND = 'AND'; + + /** + * Constant that represents an OR composite expression + */ + const TYPE_OR = 'OR'; + + /** + * @var string Holds the instance type of composite expression + */ + private $type; + + /** + * @var array Each expression part of the composite expression + */ + private $parts = array(); + + /** + * Constructor. + * + * @param string $type Instance type of composite expression + * @param array $parts Composition of expressions to be joined on composite expression + */ + public function __construct($type, array $parts = array()) + { + $this->type = $type; + + $this->addMultiple($parts); + } + + /** + * Adds multiple parts to composite expression. + * + * @param array $parts + * + * @return CompositeExpression + */ + public function addMultiple(array $parts = array()) + { + foreach ((array) $parts as $part) { + $this->add($part); + } + + return $this; + } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * @return CompositeExpression + */ + public function add($part) + { + if ( ! empty($part) || ($part instanceof self && $part->count() > 0)) { + $this->parts[] = $part; + } + + return $this; + } + + /** + * Retrieves the amount of expressions on composite expression. + * + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * Retrieve the string representation of this composite expression. + * + * @return string + */ + public function __toString() + { + if (count($this->parts) === 1) { + return (string) $this->parts[0]; + } + + return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; + } + + /** + * Return type of this composite expression (AND/OR) + * + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php new file mode 100644 index 00000000..624fc0bc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,303 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var \Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \Doctrine\DBAL\Connection $connection DBAL Connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) AND (u.role = ?) + * $expr->andX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) OR (u.role = ?) + * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x Left expression + * @param string $operator One of the ExpressionBuilder::* constants. + * @param mixed $y Right expression + * @return string + */ + public function comparison($x, $operator, $y) + { + return $x . ' ' . $operator . ' ' . $y; + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ? + * $expr->eq('u.id', '?'); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function eq($x, $y) + { + return $this->comparison($x, self::EQ, $y); + } + + /** + * Creates a non equality comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> 1 + * $q->where($q->expr()->neq('u.id', '1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function neq($x, $y) + { + return $this->comparison($x, self::NEQ, $y); + } + + /** + * Creates a lower-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ? + * $q->where($q->expr()->lt('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function lt($x, $y) + { + return $this->comparison($x, self::LT, $y); + } + + /** + * Creates a lower-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ? + * $q->where($q->expr()->lte('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function lte($x, $y) + { + return $this->comparison($x, self::LTE, $y); + } + + /** + * Creates a greater-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ? + * $q->where($q->expr()->gt('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function gt($x, $y) + { + return $this->comparison($x, self::GT, $y); + } + + /** + * Creates a greater-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ? + * $q->where($q->expr()->gte('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function gte($x, $y) + { + return $this->comparison($x, self::GTE, $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NULL + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NOT NULL + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return string + */ + public function like($x, $y) + { + return $this->comparison($x, 'LIKE', $y); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by NOT LIKE() comparison. + * @param mixed $y Argument to be used in NOT LIKE() comparison. + * + * @return string + */ + public function notLike($x, $y) + { + return $this->comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a IN () comparison expression with the given arguments. + * + * @param string $x field in string format to be inspected by IN() comparison. + * @param array $y Array of values to be used by IN() comparison. + * + * @return string + */ + public function in($x, array $y) + { + return $this->comparison($x, 'IN', '('.implode(', ', $y).')'); + } + + /** + * Creates a NOT IN () comparison expression with the given arguments. + * + * @param string $x field in string format to be inspected by NOT IN() comparison. + * @param array $y Array of values to be used by NOT IN() comparison. + * + * @return string + */ + public function notIn($x, array $y) + { + return $this->comparison($x, 'NOT IN', '('.implode(', ', $y).')'); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type Type of the parameter. + * + * @return string + */ + public function literal($input, $type = null) + { + return $this->connection->quote($input, $type); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php new file mode 100644 index 00000000..838aa010 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1099 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression, + Doctrine\DBAL\Connection; + +/** + * QueryBuilder class is responsible to dynamically create SQL queries. + * + * Important: Verify that every feature you use will work with your database vendor. + * SQL Query Builder does not attempt to validate the generated SQL at all. + * + * The query builder does no validation whatsoever if certain features even work with the + * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements + * even if some vendors such as MySQL support it. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class QueryBuilder +{ + /* The query types. */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + + /** The builder states. */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * @var \Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * @var array The array of SQL parts collected. + */ + private $sqlParts = array( + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * @var string The complete SQL string for this query. + */ + private $sql; + + /** + * @var array The query parameters. + */ + private $params = array(); + + /** + * @var array The parameter type map of this query. + */ + private $paramTypes = array(); + + /** + * @var integer The type of query this is. Can be select, update or delete. + */ + private $type = self::SELECT; + + /** + * @var integer The state of the query object. Can be dirty or clean. + */ + private $state = self::STATE_CLEAN; + + /** + * @var integer The index of the first result to retrieve. + */ + private $firstResult = null; + + /** + * @var integer The maximum number of results to retrieve. + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue) + * + * @var int + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param \Doctrine\DBAL\Connection $connection DBAL Connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function expr() + { + return $this->connection->getExpressionBuilder(); + } + + /** + * Get the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Get the associated DBAL Connection for this query builder. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Get the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Execute this query using the bound parameters and their types. + * + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * for insert, update and delete statements. + * + * @return mixed + */ + public function execute() + { + if ($this->type == self::SELECT) { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } else { + return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes); + } + } + + /** + * Get the complete SQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getSQL(); // SELECT u FROM User u + * + * + * @return string The sql query string. + */ + public function getSQL() + { + if ($this->sql !== null && $this->state === self::STATE_CLEAN) { + return $this->sql; + } + + $sql = ''; + + switch ($this->type) { + case self::DELETE: + $sql = $this->getSQLForDelete(); + break; + + case self::UPDATE: + $sql = $this->getSQLForUpdate(); + break; + + case self::SELECT: + default: + $sql = $this->getSQLForSelect(); + break; + } + + $this->state = self::STATE_CLEAN; + $this->sql = $sql; + + return $sql; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id') + * ->setParameter(':user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type PDO::PARAM_* + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + if ($type !== null) { + $this->paramTypes[$key] = $type; + } + + $this->params[$key] = $value; + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * ':user_id1' => 1, + * ':user_id2' => 2 + * )); + * + * + * @param array $params The query parameters to set. + * @param array $types The query parameters types to set. + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = array()) + { + $this->paramTypes = $types; + $this->params = $params; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return array The currently defined query parameters. + */ + public function getParameters() + { + return $this->params; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->state = self::STATE_DIRTY; + $this->firstResult = $firstResult; + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->state = self::STATE_DIRTY; + $this->maxResults = $maxResults; + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $sqlPartName + * @param string $sqlPart + * @param boolean $append + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function add($sqlPartName, $sqlPart, $append = false) + { + $isArray = is_array($sqlPart); + $isMultiple = is_array($this->sqlParts[$sqlPartName]); + + if ($isMultiple && !$isArray) { + $sqlPart = array($sqlPart); + } + + $this->state = self::STATE_DIRTY; + + if ($append) { + if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") { + foreach ($sqlPart as $part) { + $this->sqlParts[$sqlPartName][] = $part; + } + } else if ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } else if ($isMultiple) { + $this->sqlParts[$sqlPartName][] = $sqlPart; + } else { + $this->sqlParts[$sqlPartName] = $sqlPart; + } + + return $this; + } + + $this->sqlParts[$sqlPartName] = $sqlPart; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id', 'p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * @return QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, false); + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->addSelect('p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain table. + * + * + * $qb = $conn->createQueryBuilder() + * ->delete('users', 'u') + * ->where('u.id = :user_id'); + * ->setParameter(':user_id', 1); + * + * + * @param string $delete The table whose rows are subject to the deletion. + * @param string $alias The table alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', array( + 'table' => $delete, + 'alias' => $alias + )); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The table whose rows are subject to the update. + * @param string $alias The table alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', array( + 'table' => $update, + 'alias' => $alias + )); + } + + /** + * Create and add a query root corresponding to the table identified by the + * given alias, forming a cartesian product with any existing query roots. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->from('users', 'u') + * + * + * @param string $from The table + * @param string $alias The alias of the table + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias) + { + return $this->add('from', array( + 'table' => $from, + 'alias' => $alias + ), true); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function join($fromAlias, $join, $alias, $condition = null) + { + return $this->innerJoin($fromAlias, $join, $alias, $condition); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'inner', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a left join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'left', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a right join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function rightJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'right', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Sets a new value for a column in a bulk update query. + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The column to set. + * @param string $value The value, expression, placeholder, etc. + * @return QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', $key .' = ' . $value, true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $conn->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * @return QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression) ) { + $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * @return QueryBuilder This QueryBuilder instance. + * @see where() + */ + public function andWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement + * @return QueryBuilder $qb + * @see where() + */ + public function orWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.id'); + * + * + * @param mixed $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, false); + } + + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param mixed $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, true); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * @return QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) { + $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * @return QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false); + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Get a query part by its name. + * + * @param string $queryPartName + * @return mixed $queryPart + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Get all query parts. + * + * @return array $sqlParts + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Reset SQL parts + * + * @param array $queryPartNames + * @return QueryBuilder + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Reset single SQL part + * + * @param string $queryPartName + * @return QueryBuilder + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM '; + + $fromClauses = array(); + $knownAliases = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + $knownAliases[$from['alias']] = true; + $fromClause = $from['table'] . ' ' . $from['alias'] + . $this->getSQLForJoins($from['alias'], $knownAliases); + + $fromClauses[$from['alias']] = $fromClause; + } + + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($knownAliases[$fromAlias]) ) { + throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + } + } + + $query .= implode(', ', $fromClauses) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '') + . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '') + . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '') + . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : ''); + + return ($this->maxResults === null && $this->firstResult == null) + ? $query + : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult); + } + + /** + * Converts this instance into an UPDATE string in SQL. + * + * @return string + */ + private function getSQLForUpdate() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'UPDATE ' . $table + . ' SET ' . implode(", ", $this->sqlParts['set']) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Converts this instance into a DELETE string in SQL. + * + * @return string + */ + private function getSQLForDelete() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final SQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getSQL(); + } + + /** + * Create a new named parameter and bind the value $value to it. + * + * This method provides a shortcut for PDOStatement::bindValue + * when using prepared statements. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided bindValue() will automatically create a + * placeholder for you. An automatic placeholder will be of the name + * ':dcValue1', ':dcValue2' etc. + * + * For more information see {@link http://php.net/pdostatement-bindparam} + * + * Example: + * + * $value = 2; + * $q->eq( 'id', $q->bindValue( $value ) ); + * $stmt = $q->executeQuery(); // executed with 'id = 2' + * + * + * @license New BSD License + * @link http://www.zetacomponents.org + * @param mixed $value + * @param mixed $type + * @param string $placeHolder the name to bind with. The string must start with a colon ':'. + * @return string the placeholder name used. + */ + public function createNamedParameter( $value, $type = \PDO::PARAM_STR, $placeHolder = null ) + { + if ( $placeHolder === null ) { + $this->boundCounter++; + $placeHolder = ":dcValue" . $this->boundCounter; + } + $this->setParameter(substr($placeHolder, 1), $value, $type); + + return $placeHolder; + } + + /** + * Create a new positional parameter and bind the given value to it. + * + * Attention: If you are using positional parameters with the query builder you have + * to be very careful to bind all parameters in the order they appear in the SQL + * statement , otherwise they get bound in the wrong order which can lead to serious + * bugs in your code. + * + * Example: + * + * $qb = $conn->createQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR)) + * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR)) + * + * + * @param mixed $value + * @param mixed $type + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + return "?"; + } + + private function getSQLForJoins($fromAlias, array &$knownAliases) + { + $sql = ''; + + if (isset($this->sqlParts['join'][$fromAlias])) { + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + $sql .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + $knownAliases[$join['joinAlias']] = true; + + $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); + } + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php new file mode 100644 index 00000000..e2c2b328 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + static public function unknownAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not part of " . + "any FROM or JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown b/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown new file mode 100644 index 00000000..e69de29b diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php new file mode 100644 index 00000000..a06a8492 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,234 @@ +. + */ + + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Connection; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLParserUtils +{ + const POSITIONAL_TOKEN = '\?'; + const NAMED_TOKEN = '(? integer pair (indexed from zero) for a positional statement + * and a string => int[] pair for a named statement. + * + * @param string $statement + * @param bool $isPositional + * @return array + */ + static public function getPlaceholderPositions($statement, $isPositional = true) + { + $match = ($isPositional) ? '?' : ':'; + if (strpos($statement, $match) === false) { + return array(); + } + + $token = ($isPositional) ? self::POSITIONAL_TOKEN : self::NAMED_TOKEN; + $paramMap = array(); + + foreach (self::getUnquotedStatementFragments($statement) as $fragment) { + preg_match_all("/$token/", $fragment[0], $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $placeholder) { + if ($isPositional) { + $paramMap[] = $placeholder[1] + $fragment[1]; + } else { + $pos = $placeholder[1] + $fragment[1]; + $paramMap[$pos] = substr($placeholder[0], 1, strlen($placeholder[0])); + } + } + } + + return $paramMap; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query. + * @param array $types The types the previous parameters are in. + * + * @throws SQLParserUtilsException + * @return array + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + + foreach ($types as $name => $type) { + ++$bindIndex; + + if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + continue; + } + + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + + if (( ! $arrayPositions && $isPositional)) { + return array($query, $params, $types); + } + + $paramPos = self::getPlaceholderPositions($query, $isPositional); + + if ($isPositional) { + $paramOffset = 0; + $queryOffset = 0; + + foreach ($paramPos as $needle => $needlePos) { + if ( ! isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $count = count($params[$needle]); + + $params = array_merge( + array_slice($params, 0, $needle), + $params[$needle], + array_slice($params, $needle + 1) + ); + + $types = array_merge( + array_slice($types, 0, $needle), + $count ? + array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100 + array(), + array_slice($types, $needle + 1) + ); + + $expandStr = $count ? implode(", ", array_fill(0, $count, "?")) : 'NULL'; + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle. + $queryOffset += (strlen($expandStr) - 1); + } + + return array($query, $params, $types); + } + + + $queryOffset = 0; + $typesOrd = array(); + $paramsOrd = array(); + + foreach ($paramPos as $pos => $paramName) { + $paramLen = strlen($paramName) + 1; + $value = static::extractParam($paramName, $params, true); + + if ( ! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) { + $pos += $queryOffset; + $queryOffset -= ($paramLen - 1); + $paramsOrd[] = $value; + $typesOrd[] = static::extractParam($paramName, $types, false, \PDO::PARAM_STR); + $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); + + continue; + } + + $count = count($value); + $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + + foreach ($value as $val) { + $paramsOrd[] = $val; + $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET; + } + + $pos += $queryOffset; + $queryOffset += (strlen($expandStr) - $paramLen); + $query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen)); + } + + return array($query, $paramsOrd, $typesOrd); + } + + /** + * Slice the SQL statement around pairs of quotes and + * return string fragments of SQL outside of quoted literals. + * Each fragment is captured as a 2-element array: + * + * 0 => matched fragment string, + * 1 => offset of fragment in $statement + * + * @param string $statement + * @return array + */ + static private function getUnquotedStatementFragments($statement) + { + $literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' . self::ESCAPED_DOUBLE_QUOTED_TEXT; + preg_match_all("/([^'\"]+)(?:$literal)?/s", $statement, $fragments, PREG_OFFSET_CAPTURE); + + return $fragments[1]; + } + + /** + * @param string $paramName The name of the parameter (without a colon in front) + * @param array $paramsOrTypes A hash of parameters or types + * @param bool $isParam + * @param mixed $defaultValue An optional default value. If omitted, an exception is thrown + * + * @throws SQLParserUtilsException + * @return mixed + */ + static private function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null) + { + if (array_key_exists($paramName, $paramsOrTypes)) { + return $paramsOrTypes[$paramName]; + } + + // Hash keys can be prefixed with a colon for compatibility + if (array_key_exists(':' . $paramName, $paramsOrTypes)) { + return $paramsOrTypes[':' . $paramName]; + } + + if (null !== $defaultValue) { + return $defaultValue; + } + + if ($isParam) { + throw SQLParserUtilsException::missingParam($paramName); + } + + throw SQLParserUtilsException::missingType($paramName); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php new file mode 100644 index 00000000..4a74f6cd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +class SQLParserUtilsException extends DBALException +{ + public static function missingParam($paramName) + { + return new self(sprintf('Value for :%1$s not found in params array. Params array key should be "%1$s"', $paramName)); + } + + public static function missingType($typeName) + { + return new self(sprintf('Value for :%1$s not found in types array. Types array key should be "%1$s"', $typeName)); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php new file mode 100644 index 00000000..17a9c0ff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,214 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * The abstract asset allows to reset the name of all assets without publishing this to the public userland. + * + * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables + * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class AbstractAsset +{ + /** + * @var string + */ + protected $_name; + + /** + * Namespace of the asset. If none isset the default namespace is assumed. + * + * @var string + */ + protected $_namespace; + + /** + * @var bool + */ + protected $_quoted = false; + + /** + * Set name of this asset + * + * @param string $name + */ + protected function _setName($name) + { + if ($this->isIdentifierQuoted($name)) { + $this->_quoted = true; + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") !== false) { + $parts = explode(".", $name); + $this->_namespace = $parts[0]; + $name = $parts[1]; + } + $this->_name = $name; + } + + /** + * Is this asset in the default namespace? + * + * @param string $defaultNamespaceName + * @return bool + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Get namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string + */ + public function getNamespaceName() + { + return $this->_namespace; + } + + /** + * The shortest name is stripped of the default namespace. All other + * namespaced elements are returned as full-qualified names. + * + * @param string + * @return string + */ + public function getShortestName($defaultNamespaceName) + { + $shortestName = $this->getName(); + if ($this->_namespace == $defaultNamespaceName) { + $shortestName = $this->_name; + } + return strtolower($shortestName); + } + + /** + * The normalized name is full-qualified and lowerspaced. Lowerspacing is + * actually wrong, but we have to do it to keep our sanity. If you are + * using database objects that only differentiate in the casing (FOO vs + * Foo) then you will NOT be able to use Doctrine Schema abstraction. + * + * Every non-namespaced element is prefixed with the default namespace + * name which is passed as argument to this method. + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if ( ! $this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + return strtolower($name); + } + + /** + * Check if this asset's name is quoted + * + * @return bool + */ + public function isQuoted() + { + return $this->_quoted; + } + + /** + * Check if this identifier is quoted. + * + * @param string $identifier + * @return bool + */ + protected function isIdentifierQuoted($identifier) + { + return (isset($identifier[0]) && ($identifier[0] == '`' || $identifier[0] == '"')); + } + + /** + * Trim quotes from the identifier. + * + * @param string $identifier + * @return string + */ + protected function trimQuotes($identifier) + { + return str_replace(array('`', '"'), '', $identifier); + } + + /** + * Return name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + return $this->_name; + } + + /** + * Get the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getName()); + foreach ($parts as $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * Generate an identifier from a list of column names obeying a certain string length. + * + * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, + * however building idents automatically for foreign keys, composite keys or such can easily create + * very long names. + * + * @param array $columnNames + * @param string $prefix + * @param int $maxSize + * @return string + */ + protected function _generateIdentifierName($columnNames, $prefix='', $maxSize=30) + { + $hash = implode("", array_map(function($column) { + return dechex(crc32($column)); + }, $columnNames)); + return substr(strtoupper($prefix . "_" . $hash), 0, $maxSize); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php new file mode 100644 index 00000000..068d6b5d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,896 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Types; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Base class for schema managers. Schema managers are used to inspect and/or + * modify the database schema/structure. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractSchemaManager +{ + /** + * Holds instance of the Doctrine connection for this schema manager + * + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * Holds instance of the database platform used for this schema manager + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * Constructor. Accepts the Connection instance to manage the schema for + * + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(\Doctrine\DBAL\Connection $conn, AbstractPlatform $platform = null) + { + $this->_conn = $conn; + $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + } + + /** + * Return associated platform. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Try any method on the schema manager. Normally a method throws an + * exception when your DBMS doesn't support it or if an error occurs. + * This method allows you to try and method on your SchemaManager + * instance and will return false if it does not work or is not supported. + * + * + * $result = $sm->tryMethod('dropView', 'view_name'); + * + * + * @return mixed + */ + public function tryMethod() + { + $args = func_get_args(); + $method = $args[0]; + unset($args[0]); + $args = array_values($args); + + try { + return call_user_func_array(array($this, $method), $args); + } catch (\Exception $e) { + return false; + } + } + + /** + * List the available databases for this connection + * + * @return array $databases + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * List the available sequences for this connection + * + * @return Sequence[] + */ + public function listSequences($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListSequencesSQL($database); + + $sequences = $this->_conn->fetchAll($sql); + + return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); + } + + /** + * List the columns for a given table. + * + * In contrast to other libraries and to the old version of Doctrine, + * this column definition does try to contain the 'primary' field for + * the reason that it is not portable accross different RDBMS. Use + * {@see listTableIndexes($tableName)} to retrieve the primary key + * of a table. We're a RDBMS specifies more details these are held + * in the platformDetails array. + * + * @param string $table The name of the table. + * @param string $database + * @return Column[] + */ + public function listTableColumns($table, $database = null) + { + if ( ! $database) { + $database = $this->_conn->getDatabase(); + } + + $sql = $this->_platform->getListTableColumnsSQL($table, $database); + + $tableColumns = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableColumnList($table, $database, $tableColumns); + } + + /** + * List the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table + * @return Index[] $tableIndexes + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Return true if all the given tables exist. + * + * @param array $tableNames + * @return bool + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array)$tableNames); + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Return a list of all tables in the current database + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + + $tables = $this->_conn->fetchAll($sql); + $tableNames = $this->_getPortableTablesList($tables); + return $this->filterAssetNames($tableNames); + } + + /** + * Filter asset names if they are configured to return only a subset of all + * the found elements. + * + * @param array $assetNames + * @return array + */ + protected function filterAssetNames($assetNames) + { + $filterExpr = $this->getFilterSchemaAssetsExpression(); + if ( ! $filterExpr) { + return $assetNames; + } + return array_values ( + array_filter($assetNames, function ($assetName) use ($filterExpr) { + $assetName = ($assetName instanceof AbstractAsset) ? $assetName->getName() : $assetName; + return preg_match($filterExpr, $assetName); + }) + ); + } + + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * List the tables for this connection + * + * @return Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames as $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * @return Table + */ + public function listTableDetails($tableName) + { + $columns = $this->listTableColumns($tableName); + $foreignKeys = array(); + if ($this->_platform->supportsForeignKeyConstraints()) { + $foreignKeys = $this->listTableForeignKeys($tableName); + } + $indexes = $this->listTableIndexes($tableName); + + return new Table($tableName, $columns, $indexes, $foreignKeys, false, array()); + } + + /** + * List the views this connection has + * + * @return View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * List the foreign keys for the given table + * + * @param string $table The name of the table + * @return ForeignKeyConstraint[] + */ + public function listTableForeignKeys($table, $database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /* drop*() Methods */ + + /** + * Drops a database. + * + * NOTE: You can not drop the database this SchemaManager is currently connected to. + * + * @param string $database The name of the database to drop + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drop the given table + * + * @param string $table The name of the table to drop + */ + public function dropTable($table) + { + $this->_execSql($this->_platform->getDropTableSQL($table)); + } + + /** + * Drop the index from the given table + * + * @param Index|string $index The name of the index + * @param string|Table $table The name of the table + */ + public function dropIndex($index, $table) + { + if($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drop the constraint from the given table + * + * @param Constraint $constraint + * @param string $table The name of the table + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param ForeignKeyConstraint|string $table The name of the table with the foreign key. + * @param Table|string $name The name of the foreign key. + * @return boolean $result + */ + public function dropForeignKey($foreignKey, $table) + { + $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); + } + + /** + * Drops a sequence with a given name. + * + * @param string $name The name of the sequence to drop. + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drop a view + * + * @param string $name The name of the view + * @return boolean $result + */ + public function dropView($name) + { + $this->_execSql($this->_platform->getDropViewSQL($name)); + } + + /* create*() Methods */ + + /** + * Creates a new database. + * + * @param string $database The name of the database to create. + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Create a new table. + * + * @param Table $table + * @param int $createFlags + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Create a new sequence + * + * @param Sequence $sequence + * @throws \Doctrine\DBAL\ConnectionException if something fails at database level + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Create a constraint on a table + * + * @param Constraint $constraint + * @param string|Table $table + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Create a new index on a table + * + * @param Index $index + * @param string $table name of the table on which the index is to be created + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Create a new view + * + * @param View $view + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drop and create a constraint + * + * @param Constraint $constraint + * @param string $table + * @see dropConstraint() + * @see createConstraint() + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drop and create a new index on a table + * + * @param string|Table $table name of the table on which the index is to be created + * @param Index $index + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drop and create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey associative array that defines properties of the foreign key to be created. + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drop and create a new sequence + * + * @param Sequence $sequence + * @throws \Doctrine\DBAL\ConnectionException if something fails at database level + */ + public function dropAndCreateSequence(Sequence $sequence) + { + $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); + $this->createSequence($sequence); + } + + /** + * Drop and create a new table. + * + * @param Table $table + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drop and creates a new database. + * + * @param string $database The name of the database to create. + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drop and create a new view + * + * @param View $view + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alter an existing tables schema + * + * @param TableDiff $tableDiff + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries as $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Rename a given table to another name + * + * @param string $name The current name of the table + * @param string $newName The new name of the table + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * Methods for filtering return values of list*() methods to convert + * the native DBMS data definition to a portable Doctrine definition + */ + + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + /** + * @param array $sequence + * @return Sequence + */ + protected function _getPortableSequenceDefinition($sequence) + { + throw DBALException::notSupported('Sequences'); + } + + /** + * Independent of the database the keys of the column list result are lowercased. + * + * The name of the created column instance however is kept in its case. + * + * @param string $table The name of the table. + * @param string $database + * @param array $tableColumns + * @return array + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $eventManager = $this->_platform->getEventManager(); + + $list = array(); + foreach ($tableColumns as $tableColumn) { + $column = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { + $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $column = $eventArgs->getColumn(); + } + + if ( ! $defaultPrevented) { + $column = $this->_getPortableTableColumnDefinition($tableColumn); + } + + if ($column) { + $name = strtolower($column->getQuotedName($this->_platform)); + $list[$name] = $column; + } + } + return $list; + } + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregate and group the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + $result = array(); + foreach($tableIndexRows as $tableIndex) { + $indexName = $keyName = $tableIndex['key_name']; + if($tableIndex['primary']) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + if(!isset($result[$keyName])) { + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => array($tableIndex['column_name']), + 'unique' => $tableIndex['non_unique'] ? false : true, + 'primary' => $tableIndex['primary'], + 'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(), + ); + } else { + $result[$keyName]['columns'][] = $tableIndex['column_name']; + } + } + + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach($result as $indexKey => $data) { + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if ( ! $defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableDefinition($table) + { + return $table; + } + + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableUserDefinition($user) + { + return $user; + } + + protected function _getPortableViewsList($views) + { + $list = array(); + foreach ($views as $value) { + if ($view = $this->_getPortableViewDefinition($value)) { + $viewName = strtolower($view->getQuotedName($this->_platform)); + $list[$viewName] = $view; + } + } + return $list; + } + + protected function _getPortableViewDefinition($view) + { + return false; + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Create a schema instance for the current database. + * + * @return Schema + */ + public function createSchema() + { + $sequences = array(); + if($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig()); + } + + /** + * Create the configuration for this schema. + * + * @return SchemaConfig + */ + public function createSchemaConfig() + { + $schemaConfig = new SchemaConfig(); + $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + + $searchPaths = $this->getSchemaSearchPaths(); + if (isset($searchPaths[0])) { + $schemaConfig->setName($searchPaths[0]); + } + + $params = $this->_conn->getParams(); + if (isset($params['defaultTableOptions'])) { + $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); + } + + return $schemaConfig; + } + + /** + * The search path for namespaces in the currently connected database. + * + * The first entry is usually the default namespace in the Schema. All + * further namespaces contain tables/sequences which can also be addressed + * with a short, not full-qualified name. + * + * For databases that don't support subschema/namespaces this method + * returns the name of the currently connected database. + * + * @return array + */ + public function getSchemaSearchPaths() + { + return array($this->_conn->getDatabase()); + } + + /** + * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns + * the type given as default. + * + * @param string $comment + * @param string $currentType + * @return string + */ + public function extractDoctrineTypeFromComment($comment, $currentType) + { + if (preg_match("(\(DC2Type:([a-zA-Z0-9_]+)\))", $comment, $match)) { + $currentType = $match[1]; + } + return $currentType; + } + + public function removeDoctrineTypeFromComment($comment, $type) + { + return str_replace('(DC2Type:'.$type.')', '', $comment); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php new file mode 100644 index 00000000..fa6f70b3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,423 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Object representation of a database column + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var \Doctrine\DBAL\Types\Type + */ + protected $_type; + + /** + * @var int + */ + protected $_length = null; + + /** + * @var int + */ + protected $_precision = 10; + + /** + * @var int + */ + protected $_scale = 0; + + /** + * @var bool + */ + protected $_unsigned = false; + + /** + * @var bool + */ + protected $_fixed = false; + + /** + * @var bool + */ + protected $_notnull = true; + + /** + * @var string + */ + protected $_default = null; + + /** + * @var bool + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string + */ + protected $_columnDefinition = null; + + /** + * @var string + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Create a new Column + * + * @param string $columnName + * @param \Doctrine\DBAL\Types\Type $type + * @param int $length + * @param bool $notNull + * @param mixed $default + * @param bool $unsigned + * @param bool $fixed + * @param int $precision + * @param int $scale + * @param array $platformOptions + */ + public function __construct($columnName, Type $type, array $options=array()) + { + $this->_setName($columnName); + $this->setType($type); + $this->setOptions($options); + } + + /** + * @param array $options + * @return Column + */ + public function setOptions(array $options) + { + foreach ($options as $name => $value) { + $method = "set".$name; + if (method_exists($this, $method)) { + $this->$method($value); + } + } + return $this; + } + + /** + * @param Type $type + * @return Column + */ + public function setType(Type $type) + { + $this->_type = $type; + return $this; + } + + /** + * @param int $length + * @return Column + */ + public function setLength($length) + { + if($length !== null) { + $this->_length = (int)$length; + } else { + $this->_length = null; + } + return $this; + } + + /** + * @param int $precision + * @return Column + */ + public function setPrecision($precision) + { + if (!is_numeric($precision)) { + $precision = 10; // defaults to 10 when no valid precision is given. + } + + $this->_precision = (int)$precision; + return $this; + } + + /** + * @param int $scale + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int)$scale; + return $this; + } + + /** + * + * @param bool $unsigned + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool)$unsigned; + return $this; + } + + /** + * + * @param bool $fixed + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool)$fixed; + return $this; + } + + /** + * @param bool $notnull + * @return Column + */ + public function setNotnull($notnull) + { + $this->_notnull = (bool)$notnull; + return $this; + } + + /** + * + * @param mixed $default + * @return Column + */ + public function setDefault($default) + { + $this->_default = $default; + return $this; + } + + /** + * + * @param array $platformOptions + * @return Column + */ + public function setPlatformOptions(array $platformOptions) + { + $this->_platformOptions = $platformOptions; + return $this; + } + + /** + * + * @param string $name + * @param mixed $value + * @return Column + */ + public function setPlatformOption($name, $value) + { + $this->_platformOptions[$name] = $value; + return $this; + } + + /** + * + * @param string + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + return $this; + } + + public function getType() + { + return $this->_type; + } + + public function getLength() + { + return $this->_length; + } + + public function getPrecision() + { + return $this->_precision; + } + + public function getScale() + { + return $this->_scale; + } + + public function getUnsigned() + { + return $this->_unsigned; + } + + public function getFixed() + { + return $this->_fixed; + } + + public function getNotnull() + { + return $this->_notnull; + } + + public function getDefault() + { + return $this->_default; + } + + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + public function getAutoincrement() + { + return $this->_autoincrement; + } + + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + return $this; + } + + public function setComment($comment) + { + $this->_comment = $comment; + return $this; + } + + public function getComment() + { + return $this->_comment; + } + + /** + * @param string $name + * @param mixed $value + * @return Column + */ + public function setCustomSchemaOption($name, $value) + { + $this->_customSchemaOptions[$name] = $value; + return $this; + } + + /** + * @param string $name + * @return boolean + */ + public function hasCustomSchemaOption($name) + { + return isset($this->_customSchemaOptions[$name]); + } + + /** + * @param string $name + * @return mixed + */ + public function getCustomSchemaOption($name) + { + return $this->_customSchemaOptions[$name]; + } + + /** + * @param array $customSchemaOptions + * @return Column + */ + public function setCustomSchemaOptions(array $customSchemaOptions) + { + $this->_customSchemaOptions = $customSchemaOptions; + return $this; + } + + /** + * @return array + */ + public function getCustomSchemaOptions() + { + return $this->_customSchemaOptions; + } + + /** + * @param Visitor $visitor + */ + public function visit(\Doctrine\DBAL\Schema\Visitor $visitor) + { + $visitor->accept($this); + } + + /** + * @return array + */ + public function toArray() + { + return array_merge(array( + 'name' => $this->_name, + 'type' => $this->_type, + 'default' => $this->_default, + 'notnull' => $this->_notnull, + 'length' => $this->_length, + 'precision' => $this->_precision, + 'scale' => $this->_scale, + 'fixed' => $this->_fixed, + 'unsigned' => $this->_unsigned, + 'autoincrement' => $this->_autoincrement, + 'columnDefinition' => $this->_columnDefinition, + 'comment' => $this->_comment, + ), $this->_platformOptions, $this->_customSchemaOptions); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php new file mode 100644 index 00000000..2bde1bd7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represent the change of a column + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + /** + * @var Column + */ + public $fromColumn; + + public function __construct($oldColumnName, Column $column, array $changedProperties = array(), Column $fromColumn = null) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + $this->fromColumn = $fromColumn; + } + + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php new file mode 100644 index 00000000..f8c4498f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,436 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Compare to Schemas and return an instance of SchemaDiff + * + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param Schema $fromSchema + * @param Schema $toSchema + * @return SchemaDiff + */ + static public function compareSchemas( Schema $fromSchema, Schema $toSchema ) + { + $c = new self(); + return $c->compare($fromSchema, $toSchema); + } + + /** + * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. + * + * The returned differences are returned in such a way that they contain the + * operations to change the schema stored in $fromSchema to the schema that is + * stored in $toSchema. + * + * @param Schema $fromSchema + * @param Schema $toSchema + * + * @return SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + $diff->fromSchema = $fromSchema; + + $foreignKeysToTable = array(); + + foreach ( $toSchema->getTables() as $table ) { + $tableName = $table->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasTable($tableName)) { + $diff->newTables[$tableName] = $toSchema->getTable($tableName); + } else { + $tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName)); + if ($tableDifferences !== false) { + $diff->changedTables[$tableName] = $tableDifferences; + } + } + } + + /* Check if there are tables removed */ + foreach ($fromSchema->getTables() as $table) { + $tableName = $table->getShortestName($fromSchema->getName()); + + $table = $fromSchema->getTable($tableName); + if ( ! $toSchema->hasTable($tableName) ) { + $diff->removedTables[$tableName] = $table; + } + + // also remember all foreign keys that point to a specific table + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignTable = strtolower($foreignKey->getForeignTableName()); + if (!isset($foreignKeysToTable[$foreignTable])) { + $foreignKeysToTable[$foreignTable] = array(); + } + $foreignKeysToTable[$foreignTable][] = $foreignKey; + } + } + + foreach ($diff->removedTables as $tableName => $table) { + if (isset($foreignKeysToTable[$tableName])) { + $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); + + // deleting duplicated foreign keys present on both on the orphanedForeignKey + // and the removedForeignKeys from changedTables + foreach ($foreignKeysToTable[$tableName] as $foreignKey) { + // strtolower the table name to make if compatible with getShortestName + $localTableName = strtolower($foreignKey->getLocalTableName()); + if (isset($diff->changedTables[$localTableName])) { + foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { + unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); + } + } + } + } + } + + foreach ($toSchema->getSequences() as $sequence) { + $sequenceName = $sequence->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasSequence($sequenceName)) { + $diff->newSequences[] = $sequence; + } else { + if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { + $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + } + } + } + + foreach ($fromSchema->getSequences() as $sequence) { + if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { + continue; + } + + $sequenceName = $sequence->getShortestName($fromSchema->getName()); + + if ( ! $toSchema->hasSequence($sequenceName)) { + $diff->removedSequences[] = $sequence; + } + } + + return $diff; + } + + private function isAutoIncrementSequenceInSchema($schema, $sequence) + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + /** + * + * @param Sequence $sequence1 + * @param Sequence $sequence2 + */ + public function diffSequence(Sequence $sequence1, Sequence $sequence2) + { + if($sequence1->getAllocationSize() != $sequence2->getAllocationSize()) { + return true; + } + + if($sequence1->getInitialValue() != $sequence2->getInitialValue()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the tables $table1 and $table2. + * + * If there are no differences this method returns the boolean false. + * + * @param Table $table1 + * @param Table $table2 + * + * @return bool|TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + $tableDifferences->fromTable = $table1; + + $table1Columns = $table1->getColumns(); + $table2Columns = $table2->getColumns(); + + /* See if all the fields in table 1 exist in table 2 */ + foreach ( $table2Columns as $columnName => $column ) { + if ( !$table1->hasColumn($columnName) ) { + $tableDifferences->addedColumns[$columnName] = $column; + $changes++; + } + } + /* See if there are any removed fields in table 2 */ + foreach ( $table1Columns as $columnName => $column ) { + if ( !$table2->hasColumn($columnName) ) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + } + } + + foreach ( $table1Columns as $columnName => $column ) { + if ( $table2->hasColumn($columnName) ) { + $changedProperties = $this->diffColumn( $column, $table2->getColumn($columnName) ); + if (count($changedProperties) ) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $columnDiff->fromColumn = $column; + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + foreach ($table2Indexes as $index2Name => $index2Definition) { + foreach ($table1Indexes as $index1Name => $index1Definition) { + if ($this->diffIndex($index1Definition, $index2Definition) === false) { + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + } else { + if ($index1Name == $index2Name) { + $tableDifferences->changedIndexes[$index2Name] = $table2Indexes[$index2Name]; + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + $changes++; + } + } + } + } + + foreach ($table1Indexes as $index1Name => $index1Definition) { + $tableDifferences->removedIndexes[$index1Name] = $index1Definition; + $changes++; + } + + foreach ($table2Indexes as $index2Name => $index2Definition) { + $tableDifferences->addedIndexes[$index2Name] = $index2Definition; + $changes++; + } + + $fromFkeys = $table1->getForeignKeys(); + $toFkeys = $table2->getForeignKeys(); + + foreach ($fromFkeys as $key1 => $constraint1) { + foreach ($toFkeys as $key2 => $constraint2) { + if($this->diffForeignKey($constraint1, $constraint2) === false) { + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } else { + if (strtolower($constraint1->getName()) == strtolower($constraint2->getName())) { + $tableDifferences->changedForeignKeys[] = $constraint2; + $changes++; + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } + } + } + } + + foreach ($fromFkeys as $key1 => $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys as $key2 => $constraint2) { + $tableDifferences->addedForeignKeys[] = $constraint2; + $changes++; + } + + return $changes ? $tableDifferences : false; + } + + /** + * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param TableDiff $tableDifferences + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns as $removedColumnName => $removedColumn) { + if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { + $renameCandidates[$addedColumn->getName()][] = array($removedColumn, $addedColumn, $addedColumnName); + } + } + } + + foreach ($renameCandidates as $candidateColumns) { + if (count($candidateColumns) == 1) { + list($removedColumn, $addedColumn) = $candidateColumns[0]; + $removedColumnName = strtolower($removedColumn->getName()); + $addedColumnName = strtolower($addedColumn->getName()); + + if ( ! isset($tableDifferences->renamedColumns[$removedColumnName])) { + $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + unset($tableDifferences->addedColumns[$addedColumnName]); + unset($tableDifferences->removedColumns[$removedColumnName]); + } + } + } + } + + /** + * @param ForeignKeyConstraint $key1 + * @param ForeignKeyConstraint $key2 + * @return bool + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getLocalColumns()) != array_map('strtolower', $key2->getLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getForeignColumns()) != array_map('strtolower', $key2->getForeignColumns())) { + return true; + } + + if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { + return true; + } + + if ($key1->onUpdate() != $key2->onUpdate()) { + return true; + } + + if ($key1->onDelete() != $key2->onDelete()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the fields $field1 and $field2. + * + * If there are differences this method returns $field2, otherwise the + * boolean false. + * + * @param Column $column1 + * @param Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $changedProperties = array(); + if ( $column1->getType() != $column2->getType() ) { + $changedProperties[] = 'type'; + } + + if ($column1->getNotnull() != $column2->getNotnull()) { + $changedProperties[] = 'notnull'; + } + + if ($column1->getDefault() != $column2->getDefault()) { + $changedProperties[] = 'default'; + } + + if ($column1->getUnsigned() != $column2->getUnsigned()) { + $changedProperties[] = 'unsigned'; + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\StringType) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $column1->getLength() ?: 255; + $length2 = $column2->getLength() ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($column1->getFixed() != $column2->getFixed()) { + $changedProperties[] = 'fixed'; + } + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\DecimalType) { + if (($column1->getPrecision()?:10) != ($column2->getPrecision()?:10)) { + $changedProperties[] = 'precision'; + } + if ($column1->getScale() != $column2->getScale()) { + $changedProperties[] = 'scale'; + } + } + + if ($column1->getAutoincrement() != $column2->getAutoincrement()) { + $changedProperties[] = 'autoincrement'; + } + + // only allow to delete comment if its set to '' not to null. + if ($column1->getComment() !== null && $column1->getComment() != $column2->getComment()) { + $changedProperties[] = 'comment'; + } + + $options1 = $column1->getCustomSchemaOptions(); + $options2 = $column2->getCustomSchemaOptions(); + + $commonKeys = array_keys(array_intersect_key($options1, $options2)); + + foreach ($commonKeys as $key) { + if ($options1[$key] !== $options2[$key]) { + $changedProperties[] = $key; + } + } + + $diffKeys = array_keys(array_diff_key($options1, $options2) + array_diff_key($options2, $options1)); + + $changedProperties = array_merge($changedProperties, $diffKeys); + + return $changedProperties; + } + + /** + * Finds the difference between the indexes $index1 and $index2. + * + * Compares $index1 with $index2 and returns $index2 if there are any + * differences or false in case there are no differences. + * + * @param Index $index1 + * @param Index $index2 + * @return bool + */ + public function diffIndex(Index $index1, Index $index2) + { + if ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1)) { + return false; + } + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php new file mode 100644 index 00000000..f80410f2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Marker interface for contraints + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Constraint +{ + public function getName(); + + public function getQuotedName(AbstractPlatform $platform); + + public function getColumns(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php new file mode 100644 index 00000000..e11c64cb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,214 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Events; + +/** + * IBM Db2 Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * Return a list of all tables in the current database + * + * Apparently creator is the schema not the user who created it: + * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); + + switch (strtolower($tableColumn['typename'])) { + case 'varchar': + $length = $tableColumn['length']; + $fixed = false; + break; + case 'character': + $length = $tableColumn['length']; + $fixed = true; + break; + case 'clob': + $length = $tableColumn['length']; + break; + case 'decimal': + case 'double': + case 'real': + $scale = $tableColumn['scale']; + $precision = $tableColumn['length']; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool)$unsigned, + 'fixed' => (bool)$fixed, + 'default' => ($tableColumn['default'] == "NULL") ? null : $tableColumn['default'], + 'notnull' => (bool) ($tableColumn['nulls'] == 'N'), + 'scale' => null, + 'precision' => null, + 'platformOptions' => array(), + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['colname'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables as $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + return $tableNames; + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach($tableIndexes as $indexKey => $data) { + $data = array_change_key_case($data, \CASE_LOWER); + $unique = ($data['uniquerule'] == "D") ? false : true; + $primary = ($data['uniquerule'] == "P"); + + $indexName = strtolower($data['name']); + if ($primary) { + $keyName = 'primary'; + } else { + $keyName = $indexName; + } + + $data = array( + 'name' => $indexName, + 'columns' => explode("+", ltrim($data['colnames'], '+')), + 'unique' => $unique, + 'primary' => $primary + ); + + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if ( ! $defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); + + $tableForeignKey['deleterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['deleterule']); + $tableForeignKey['updaterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['updaterule']); + + return new ForeignKeyConstraint( + array_map('trim', (array)$tableForeignKey['fkcolnames']), + $tableForeignKey['reftbname'], + array_map('trim', (array)$tableForeignKey['pkcolnames']), + $tableForeignKey['relname'], + array( + 'onUpdate' => $tableForeignKey['updaterule'], + 'onDelete' => $tableForeignKey['deleterule'], + ) + ); + } + + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } else if ($def == "N") { + return "SET NULL"; + } + return null; + } + + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + // sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199 + //$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']); + if (!is_resource($view['text'])) { + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + } else { + $sql = ''; + } + + return new View($view['name'], $sql); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php new file mode 100644 index 00000000..f73b223c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Schema manager for the Drizzle RDBMS. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableName = $tableColumn['COLUMN_NAME']; + $dbType = strtolower($tableColumn['DATA_TYPE']); + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + + $options = array( + 'notnull' => !(bool)$tableColumn['IS_NULLABLE'], + 'length' => (int)$tableColumn['CHARACTER_MAXIMUM_LENGTH'], + 'default' => empty($tableColumn['COLUMN_DEFAULT']) ? null : $tableColumn['COLUMN_DEFAULT'], + 'autoincrement' => (bool)$tableColumn['IS_AUTO_INCREMENT'], + 'scale' => (int)$tableColumn['NUMERIC_SCALE'], + 'precision' => (int)$tableColumn['NUMERIC_PRECISION'], + 'comment' => (isset($tableColumn['COLUMN_COMMENT']) ? $tableColumn['COLUMN_COMMENT'] : null), + ); + + return new Column($tableName, \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['SCHEMA_NAME']; + } + + protected function _getPortableTableDefinition($table) + { + return $table['TABLE_NAME']; + } + + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $columns = array(); + foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { + $columns[] = trim($value, ' `'); + } + + $ref_columns = array(); + foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { + $ref_columns[] = trim($value, ' `'); + } + + return new ForeignKeyConstraint( + $columns, + $tableForeignKey['REFERENCED_TABLE_NAME'], + $ref_columns, + $tableForeignKey['CONSTRAINT_NAME'], + array( + 'onUpdate' => $tableForeignKey['UPDATE_RULE'], + 'onDelete' => $tableForeignKey['DELETE_RULE'], + ) + ); + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + $indexes = array(); + foreach ($tableIndexes as $k) { + $k['primary'] = (boolean)$k['primary']; + $indexes[] = $k; + } + + return parent::_getPortableTableIndexesList($indexes, $tableName); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php new file mode 100644 index 00000000..d9c7af92 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,212 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * @var Table + */ + protected $_localTable; + + /** + * @var array + */ + protected $_localColumnNames; + + /** + * @var string + */ + protected $_foreignTableName; + + /** + * @var array + */ + protected $_foreignColumnNames; + + /** + * @var string + */ + protected $_cascade = ''; + + /** + * @var array + */ + protected $_options; + + /** + * + * @param array $localColumnNames + * @param string $foreignTableName + * @param array $foreignColumnNames + * @param string $cascade + * @param string|null $name + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name=null, array $options=array()) + { + $this->_setName($name); + $this->_localColumnNames = $localColumnNames; + $this->_foreignTableName = $foreignTableName; + $this->_foreignColumnNames = $foreignColumnNames; + $this->_options = $options; + } + + /** + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * @param Table $table + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return Table + */ + public function getLocalTable() + { + return $this->_localTable; + } + + /** + * @return array + */ + public function getLocalColumns() + { + return $this->_localColumnNames; + } + + public function getColumns() + { + return $this->_localColumnNames; + } + + /** + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName; + } + + /** + * Return the non-schema qualified foreign table name. + * + * @return string + */ + public function getUnqualifiedForeignTableName() + { + $parts = explode(".", $this->_foreignTableName); + return strtolower(end($parts)); + } + + /** + * Get the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedForeignTableName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getForeignTableName()); + foreach ($parts AS $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * @return array + */ + public function getForeignColumns() + { + return $this->_foreignColumnNames; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Gets the options associated with this constraint + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Foreign Key onUpdate status + * + * @return string|null + */ + public function onUpdate() + { + return $this->_onEvent('onUpdate'); + } + + /** + * Foreign Key onDelete status + * + * @return string|null + */ + public function onDelete() + { + return $this->_onEvent('onDelete'); + } + + /** + * @param string $event + * @return string|null + */ + private function _onEvent($event) + { + if (isset($this->_options[$event])) { + $onEvent = strtoupper($this->_options[$event]); + if (!in_array($onEvent, array('NO ACTION', 'RESTRICT'))) { + return $onEvent; + } + } + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php new file mode 100644 index 00000000..8e66e489 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +class Index extends AbstractAsset implements Constraint +{ + /** + * @var array + */ + protected $_columns; + + /** + * @var bool + */ + protected $_isUnique = false; + + /** + * @var bool + */ + protected $_isPrimary = false; + + /** + * Platform specific flags for indexes. + * + * @var array + */ + protected $_flags = array(); + + /** + * @param string $indexName + * @param array $column + * @param bool $isUnique + * @param bool $isPrimary + */ + public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array()) + { + $isUnique = ($isPrimary)?true:$isUnique; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * @param string $column + */ + protected function _addColumn($column) + { + if(is_string($column)) { + $this->_columns[] = $column; + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getUnquotedColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getColumns()); + } + + /** + * Is the index neither unique nor primary key? + * + * @return bool + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return bool + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return bool + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param int $pos + * @return bool + */ + public function hasColumnAtPosition($columnName, $pos = 0) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); + return array_search($columnName, $indexColumns) === $pos; + } + + /** + * Check if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $sameColumns = true; + for ($i = 0; $i < count($this->_columns); $i++) { + if (!isset($columnNames[$i]) || $this->trimQuotes(strtolower($this->_columns[$i])) != $this->trimQuotes(strtolower($columnNames[$i]))) { + $sameColumns = false; + } + } + return $sameColumns; + } + + /** + * Check if the other index already fulfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * @return bool + */ + public function isFullfilledBy(Index $other) + { + // allow the other index to be equally large only. It being larger is an option + // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) + if (count($other->getColumns()) != count($this->getColumns())) { + return false; + } + + // Check if columns are the same, and even in the same order + $sameColumns = $this->spansColumns($other->getColumns()); + + if ($sameColumns) { + if ( ! $this->isUnique() && !$this->isPrimary()) { + // this is a special case: If the current key is neither primary or unique, any uniqe or + // primary key will always have the same effect for the index and there cannot be any constraint + // overlaps. This means a primary or unique index can always fulfill the requirements of just an + // index that has no constraints. + return true; + } else if ($other->isPrimary() != $this->isPrimary()) { + return false; + } else if ($other->isUnique() != $this->isUnique()) { + return false; + } + return true; + } + return false; + } + + /** + * Detect if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * @return bool + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } else if ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique())) { + return true; + } + return false; + } + + /** + * Returns platform specific flags for indexes. + * + * @return array + */ + public function getFlags() + { + return array_keys($this->_flags); + } + + /** + * Add Flag for an index that translates to platform specific handling. + * + * @example $index->addFlag('CLUSTERED') + * @param string $flag + * @return Index + */ + public function addFlag($flag) + { + $this->flags[strtolower($flag)] = true; + return $this; + } + + /** + * Does this index have a specific flag? + * + * @param string $flag + * @return bool + */ + public function hasFlag($flag) + { + return isset($this->flags[strtolower($flag)]); + } + + /** + * Remove a flag + * + * @param string $flag + * @return void + */ + public function removeFlag($flag) + { + unset($this->flags[strtolower($flag)]); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php new file mode 100644 index 00000000..3c422920 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,212 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Schema manager for the MySql RDBMS. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + foreach($tableIndexes as $k => $v) { + $v = array_change_key_case($v, CASE_LOWER); + if($v['key_name'] == 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = array('FULLTEXT'); + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * Gets a portable column definition. + * + * The database type is mapped to a corresponding Doctrine mapping type. + * + * @param $tableColumn + * @return array + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['type']); + $dbType = strtok($dbType, '(), '); + if (isset($tableColumn['length'])) { + $length = $tableColumn['length']; + $decimal = ''; + } else { + $length = strtok('(), '); + $decimal = strtok('(), ') ? strtok('(), '):null; + } + $type = array(); + $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + // In cases where not connected to a database DESCRIBE $table does not return 'Comment' + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'numeric': + case 'decimal': + if(preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'blob': + case 'year': + $length = null; + break; + } + + $length = ((int) $length == 0) ? null : (int) $length; + + $options = array( + 'length' => $length, + 'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false), + 'fixed' => (bool) $fixed, + 'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null, + 'notnull' => (bool) ($tableColumn['null'] != 'YES'), + 'scale' => null, + 'precision' => null, + 'autoincrement' => (bool) (strpos($tableColumn['extra'], 'auto_increment') !== false), + 'comment' => (isset($tableColumn['comment'])) ? $tableColumn['comment'] : null + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $value) { + $value = array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if (!isset($value['delete_rule']) || $value['delete_rule'] == "RESTRICT") { + $value['delete_rule'] = null; + } + if (!isset($value['update_rule']) || $value['update_rule'] == "RESTRICT") { + $value['update_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['referenced_table_name'], + 'onDelete' => $value['delete_rule'], + 'onUpdate' => $value['update_rule'], + ); + } + $list[$value['constraint_name']]['local'][] = $value['column_name']; + $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; + } + + $result = array(); + foreach($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + ) + ); + } + + return $result; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php new file mode 100644 index 00000000..2a880d9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,286 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Oracle Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($view['view_name'], $view['text']); + } + + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $table['table_name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + foreach ( $tableIndexes as $tableIndex ) { + $tableIndex = \array_change_key_case($tableIndex, CASE_LOWER); + + $keyName = strtolower($tableIndex['name']); + + if ( strtolower($tableIndex['is_primary']) == "p" ) { + $keyName = 'primary'; + $buffer['primary'] = true; + $buffer['non_unique'] = false; + } else { + $buffer['primary'] = false; + $buffer['non_unique'] = ( $tableIndex['is_unique'] == 0 ) ? true : false; + } + $buffer['key_name'] = $keyName; + $buffer['column_name'] = $tableIndex['column_name']; + $indexBuffer[] = $buffer; + } + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = \array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['data_type']); + if(strpos($dbType, "timestamp(") === 0) { + if (strpos($dbType, "WITH TIME ZONE")) { + $dbType = "timestamptz"; + } else { + $dbType = "timestamp"; + } + } + + $type = array(); + $length = $unsigned = $fixed = null; + if ( ! empty($tableColumn['data_length'])) { + $length = $tableColumn['data_length']; + } + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + if (stripos($tableColumn['data_default'], 'NULL') !== null) { + $tableColumn['data_default'] = null; + } + + $precision = null; + $scale = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); + $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); + + switch ($dbType) { + case 'number': + if ($tableColumn['data_precision'] == 20 && $tableColumn['data_scale'] == 0) { + $precision = 20; + $scale = 0; + $type = 'bigint'; + } elseif ($tableColumn['data_precision'] == 5 && $tableColumn['data_scale'] == 0) { + $type = 'smallint'; + $precision = 5; + $scale = 0; + } elseif ($tableColumn['data_precision'] == 1 && $tableColumn['data_scale'] == 0) { + $precision = 1; + $scale = 0; + $type = 'boolean'; + } elseif ($tableColumn['data_scale'] > 0) { + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $type = 'decimal'; + } + $length = null; + break; + case 'pls_integer': + case 'binary_integer': + $length = null; + break; + case 'varchar': + case 'varchar2': + case 'nvarchar2': + $length = $tableColumn['char_length']; + $fixed = false; + break; + case 'char': + case 'nchar': + $length = $tableColumn['char_length']; + $fixed = true; + break; + case 'date': + case 'timestamp': + $length = null; + break; + case 'float': + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $length = null; + break; + case 'clob': + case 'nclob': + $length = null; + break; + case 'blob': + case 'raw': + case 'long raw': + case 'bfile': + $length = null; + break; + case 'rowid': + case 'urowid': + default: + $length = null; + } + + $options = array( + 'notnull' => (bool) ($tableColumn['nullable'] === 'N'), + 'fixed' => (bool) $fixed, + 'unsigned' => (bool) $unsigned, + 'default' => $tableColumn['data_default'], + 'length' => $length, + 'precision' => $precision, + 'scale' => $scale, + 'comment' => (isset($tableColumn['comments'])) ? $tableColumn['comments'] : null, + 'platformDetails' => array(), + ); + + return new Column($tableColumn['column_name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = \array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if ($value['delete_rule'] == "NO ACTION") { + $value['delete_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + $list[$value['constraint_name']]['local'][$value['position']] = $value['local_column']; + $list[$value['constraint_name']]['foreign'][$value['position']] = $value['foreign_column']; + } + + $result = array(); + foreach($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array('onDelete' => $constraint['onDelete']) + ); + } + + return $result; + } + + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['min_value']); + } + + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + return $function['name']; + } + + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + return $database['username']; + } + + public function createDatabase($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $username = $database; + $password = $params['password']; + + $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; + $result = $this->_conn->executeUpdate($query); + + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $result = $this->_conn->executeUpdate($query); + + return true; + } + + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + public function dropTable($name) + { + $this->dropAutoincrement($name); + + return parent::dropTable($name); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php new file mode 100644 index 00000000..3f8ffd48 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,369 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * PostgreSQL Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Get all the existing schema names. + * + * @return array + */ + public function getSchemaNames() + { + $rows = $this->_conn->fetchAll("SELECT nspname as schema_name FROM pg_namespace WHERE nspname !~ '^pg_.*' and nspname != 'information_schema'"); + return array_map(function($v) { return $v['schema_name']; }, $rows); + } + + /** + * Return an array of schema search paths + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getSchemaSearchPaths() + { + $params = $this->_conn->getParams(); + $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path')); + + if (isset($params['user'])) { + $schema = str_replace('"$user"', $params['user'], $schema); + } + + return array_map('trim', $schema); + } + + /** + * Get names of all existing schemas in the current users search path. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getExistingSchemaSearchPaths() + { + if ($this->existingSchemaPaths === null) { + $this->determineExistingSchemaSearchPaths(); + } + return $this->existingSchemaPaths; + } + + /** + * Use this to set or reset the order of the existing schemas in the current search path of the user + * + * This is a PostgreSQL only function. + * + * @return void + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->getSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) { + return in_array($v, $names); + }); + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $onUpdate = null; + $onDelete = null; + + if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onUpdate = $match[1]; + } + if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onDelete = $match[1]; + } + + if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) { + // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get + // the idea to trim them here. + $localColumns = array_map('trim', explode(",", $values[1])); + $foreignColumns = array_map('trim', explode(",", $values[3])); + $foreignTable = $values[2]; + } + + return new ForeignKeyConstraint( + $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], + array('onUpdate' => $onUpdate, 'onDelete' => $onDelete) + ); + } + + public function dropDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::dropDatabase($database); + + $this->_conn->close(); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::createDatabase($database); + + $this->_conn->close(); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['viewname'], $view['definition']); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + protected function _getPortableTableDefinition($table) + { + $schemas = $this->getExistingSchemaSearchPaths(); + $firstSchema = array_shift($schemas); + + if ($table['schema_name'] == $firstSchema) { + return $table['table_name']; + } else { + return $table['schema_name'] . "." . $table['table_name']; + } + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $buffer = array(); + foreach ($tableIndexes as $row) { + $colNumbers = explode(' ', $row['indkey']); + $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )'; + $columnNameSql = "SELECT attnum, attname FROM pg_attribute + WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;"; + + $stmt = $this->_conn->executeQuery($columnNameSql); + $indexColumns = $stmt->fetchAll(); + + // required for getting the order of the columns right. + foreach ($colNumbers as $colNum) { + foreach ($indexColumns as $colRow) { + if ($colNum == $colRow['attnum']) { + $buffer[] = array( + 'key_name' => $row['relname'], + 'column_name' => trim($colRow['attname']), + 'non_unique' => !$row['indisunique'], + 'primary' => $row['indisprimary'] + ); + } + } + } + } + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + protected function _getPortableSequenceDefinition($sequence) + { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); + $tableColumn['length'] = $length; + } + + $matches = array(); + + $autoincrement = false; + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { + $tableColumn['sequence'] = $matches[1]; + $tableColumn['default'] = null; + $autoincrement = true; + } + + if (preg_match("/^'(.*)'::.*$/", $tableColumn['default'], $matches)) { + $tableColumn['default'] = $matches[1]; + } + + if (stripos($tableColumn['default'], 'NULL') === 0) { + $tableColumn['default'] = null; + } + + $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; + if ($length == '-1' && isset($tableColumn['atttypmod'])) { + $length = $tableColumn['atttypmod'] - 4; + } + if ((int) $length <= 0) { + $length = null; + } + $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + $dbType = strtolower($tableColumn['type']); + if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { + $dbType = strtolower($tableColumn['domain_type']); + $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'smallint': + case 'int2': + $length = null; + break; + case 'int': + case 'int4': + case 'integer': + $length = null; + break; + case 'bigint': + case 'int8': + $length = null; + break; + case 'bool': + case 'boolean': + $length = null; + break; + case 'text': + $fixed = false; + break; + case 'varchar': + case 'interval': + case '_varchar': + $fixed = false; + break; + case 'char': + case 'bpchar': + $fixed = true; + break; + case 'float': + case 'float4': + case 'float8': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'money': + case 'numeric': + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'year': + $length = null; + break; + } + + if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { + $tableColumn['default'] = $match[1]; + } + + $options = array( + 'length' => $length, + 'notnull' => (bool) $tableColumn['isnotnull'], + 'default' => $tableColumn['default'], + 'primary' => (bool) ($tableColumn['pri'] == 't'), + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => $tableColumn['comment'], + ); + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php new file mode 100644 index 00000000..79c7526a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,256 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException; +use Doctrine\DBAL\Types\Type; + +/** + * SQL Server Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @author Steve Müller + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['name'], $sequence['increment'], $sequence['start_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtok($tableColumn['type'], '(), '); + $fixed = null; + $length = (int) $tableColumn['length']; + $default = $tableColumn['default']; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + + if ($default == 'getdate()') { + $default = $this->_platform->getCurrentTimestampSQL(); + } + } + + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + case 'varchar': + // TEXT type is returned as VARCHAR(MAX) with a length of -1 + if ($length == -1) { + $dbType = 'text'; + } + break; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + switch ($type) { + case 'char': + $fixed = true; + break; + case 'text': + $fixed = false; + break; + } + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => false, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) $tableColumn['notnull'], + 'scale' => $tableColumn['scale'], + 'precision' => $tableColumn['precision'], + 'autoincrement' => (bool) $tableColumn['autoincrement'], + ); + + $platformOptions = array( + 'collate' => $tableColumn['collation'] == 'NULL' ? null : $tableColumn['collation'] + ); + + $column = new Column($tableColumn['name'], Type::getType($type), $options); + $column->setPlatformOptions($platformOptions); + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if ( ! isset($foreignKeys[$tableForeignKey['ForeignKey']])) { + $foreignKeys[$tableForeignKey['ForeignKey']] = array( + 'local_columns' => array($tableForeignKey['ColumnName']), + 'foreign_table' => $tableForeignKey['ReferenceTableName'], + 'foreign_columns' => array($tableForeignKey['ReferenceColumnName']), + 'name' => $tableForeignKey['ForeignKey'], + 'options' => array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']) + ) + ); + } else { + $foreignKeys[$tableForeignKey['ForeignKey']]['local_columns'][] = $tableForeignKey['ColumnName']; + $foreignKeys[$tableForeignKey['ForeignKey']]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['non_unique'] = (boolean) $tableIndex['non_unique']; + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = $tableIndex['flags'] ? array($tableIndex['flags']) : null; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * @override + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @override + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * @override + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * List the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table + * @return Index[] $tableIndexes + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + try { + $tableIndexes = $this->_conn->fetchAll($sql); + } catch(\PDOException $e) { + if ($e->getCode() == "IMSSP") { + return array(); + } else { + throw $e; + } + } catch(SQLSrvException $e) { + if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { + return array(); + } else { + throw $e; + } + } + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * @override + */ + public function alterTable(TableDiff $tableDiff) + { + if(count($tableDiff->removedColumns) > 0) { + foreach($tableDiff->removedColumns as $col){ + $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); + foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) { + $this->_conn->exec("ALTER TABLE $tableDiff->name DROP CONSTRAINT " . $constraint['Name']); + } + } + } + + return parent::alterTable($tableDiff); + } + + /** + * This function retrieves the constraints for a given column. + */ + private function getColumnConstraintSQL($table, $column) + { + return "SELECT SysObjects.[Name] + FROM SysObjects INNER JOIN (SELECT [Name],[ID] FROM SysObjects WHERE XType = 'U') AS Tab + ON Tab.[ID] = Sysobjects.[Parent_Obj] + INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = Sysobjects.[ID] + INNER JOIN SysColumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID] + WHERE Col.[Name] = " . $this->_conn->quote($column) ." AND Tab.[Name] = " . $this->_conn->quote($table) . " + ORDER BY Col.[Name]"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php new file mode 100644 index 00000000..5a6ff751 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,373 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Object representation of a database schema + * + * Different vendors have very inconsistent naming with regard to the concept + * of a "schema". Doctrine understands a schema as the entity that conceptually + * wraps a set of database objects such as tables, sequences, indexes and + * foreign keys that belong to each other into a namespace. A Doctrine Schema + * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more + * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL. + * + * Every asset in the doctrine schema has a name. A name consists of either a + * namespace.local name pair or just a local unqualified name. + * + * The abstraction layer that covers a PostgreSQL schema is the namespace of an + * database object (asset). A schema can have a name, which will be used as + * default namespace for the unqualified database objects that are created in + * the schema. + * + * In the case of MySQL where cross-database queries are allowed this leads to + * databases being "misinterpreted" as namespaces. This is intentional, however + * the CREATE/DROP SQL visitors will just filter this queries and do not + * execute them. Only the queries for the currently connected database are + * executed. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Schema extends AbstractAsset +{ + /** + * @var array + */ + protected $_tables = array(); + + /** + * @var array + */ + protected $_sequences = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param array $tables + * @param array $sequences + * @param array $views + * @param array $triggers + * @param SchemaConfig $schemaConfig + */ + public function __construct(array $tables=array(), array $sequences=array(), SchemaConfig $schemaConfig=null) + { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($tables as $table) { + $this->_addTable($table); + } + + foreach ($sequences as $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param Table $table + */ + protected function _addTable(Table $table) + { + $tableName = $table->getFullQualifiedName($this->getName()); + if(isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param Sequence $sequence + */ + protected function _addSequence(Sequence $sequence) + { + $seqName = $sequence->getFullQualifiedName($this->getName()); + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + $this->_sequences[$seqName] = $sequence; + } + + /** + * Get all tables of this schema. + * + * @return array + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * @return Table + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @return string + */ + private function getFullQualifiedAssetName($name) + { + if ($this->isIdentifierQuoted($name)) { + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + return strtolower($name); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * @return Schema + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + return isset($this->_tables[$tableName]); + } + + /** + * Get all table names, prefixed with a schema name, even the default one + * if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + return isset($this->_sequences[$sequenceName]); + } + + /** + * @throws SchemaException + * @param string $sequenceName + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function getSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + if(!$this->hasSequence($sequenceName)) { + throw SchemaException::sequenceDoesNotExist($sequenceName); + } + return $this->_sequences[$sequenceName]; + } + + /** + * @return \Doctrine\DBAL\Schema\Sequence[] + */ + public function getSequences() + { + return $this->_sequences; + } + + /** + * Create a new table + * + * @param string $tableName + * @return Table + */ + public function createTable($tableName) + { + $table = new Table($tableName); + $this->_addTable($table); + + foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) { + $table->addOption($name, $value); + } + + return $table; + } + + /** + * Rename a table + * + * @param string $oldTableName + * @param string $newTableName + * @return Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + return $this; + } + + /** + * Drop a table from the schema. + * + * @param string $tableName + * @return Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $table = $this->getTable($tableName); + unset($this->_tables[$tableName]); + return $this; + } + + /** + * Create a new sequence + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * @return Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + return $seq; + } + + /** + * @param string $sequenceName + * @return Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + return $this; + } + + /** + * Return an array of necessary sql queries to create the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @return array + */ + public function toSql(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $sqlCollector = new CreateSchemaSqlCollector($platform); + $this->visit($sqlCollector); + + return $sqlCollector->getQueries(); + } + + /** + * Return an array of necessary sql queries to drop the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @return array + */ + public function toDropSql(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param Schema $toSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function getMigrateToSql(Schema $toSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + return $schemaDiff->toSql($platform); + } + + /** + * @param Schema $fromSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function getMigrateFromSql(Schema $fromSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + return $schemaDiff->toSql($platform); + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + foreach ($this->_tables as $table) { + $table->visit($visitor); + } + foreach ($this->_sequences as $sequence) { + $sequence->visit($visitor); + } + } + + /** + * Cloning a Schema triggers a deep clone of all related assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_tables as $k => $table) { + $this->_tables[$k] = clone $table; + } + foreach ($this->_sequences as $k => $sequence) { + $this->_sequences[$k] = clone $sequence; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php new file mode 100644 index 00000000..d90da206 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var bool + */ + protected $hasExplicitForeignKeyIndexes = false; + + /** + * @var int + */ + protected $maxIdentifierLength = 63; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $defaultTableOptions = array(); + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->hasExplicitForeignKeyIndexes; + } + + /** + * @param bool $flag + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->hasExplicitForeignKeyIndexes = (bool)$flag; + } + + /** + * @param int $length + */ + public function setMaxIdentifierLength($length) + { + $this->maxIdentifierLength = (int)$length; + } + + /** + * @return int + */ + public function getMaxIdentifierLength() + { + return $this->maxIdentifierLength; + } + + /** + * Get default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * set default namespace name of schema objects. + * + * @param string $name the value to set. + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get the default options that are passed to Table instances created with + * Schema#createTable(). + * + * @return array + */ + public function getDefaultTableOptions() + { + return $this->defaultTableOptions; + } + + public function setDefaultTableOptions(array $defaultTableOptions) + { + $this->defaultTableOptions = $defaultTableOptions; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php new file mode 100644 index 00000000..fa72de02 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Schema Diff + * + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaDiff +{ + /** + * @var Schema + */ + public $fromSchema; + + /** + * All added tables + * + * @var array(string=>ezcDbSchemaTable) + */ + public $newTables = array(); + + /** + * All changed tables + * + * @var array(string=>ezcDbSchemaTableDiff) + */ + public $changedTables = array(); + + /** + * All removed tables + * + * @var array(string=>Table) + */ + public $removedTables = array(); + + /** + * @var array + */ + public $newSequences = array(); + + /** + * @var array + */ + public $changedSequences = array(); + + /** + * @var array + */ + public $removedSequences = array(); + + /** + * @var array + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param array(string=>Table) $newTables + * @param array(string=>TableDiff) $changedTables + * @param array(string=>bool) $removedTables + * @param Schema $fromSchema + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array(), Schema $fromSchema = null) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + $this->fromSchema = $fromSchema; + } + + /** + * The to save sql mode ensures that the following things don't happen: + * + * 1. Tables are deleted + * 2. Sequences are deleted + * 3. Foreign Keys which reference tables that would otherwise be deleted. + * + * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. + * + * @param AbstractPlatform $platform + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param AbstractPlatform $platform + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param AbstractPlatform $platform + * @param bool $saveMode + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + if ($platform->supportsForeignKeyConstraints() && $saveMode == false) { + foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { + $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName()); + } + } + + if ($platform->supportsSequences() == true) { + foreach ($this->changedSequences as $sequence) { + $sql[] = $platform->getAlterSequenceSQL($sequence); + } + + if ($saveMode === false) { + foreach ($this->removedSequences as $sequence) { + $sql[] = $platform->getDropSequenceSQL($sequence); + } + } + + foreach ($this->newSequences as $sequence) { + $sql[] = $platform->getCreateSequenceSQL($sequence); + } + } + + $foreignKeySql = array(); + foreach ($this->newTables as $table) { + $sql = array_merge( + $sql, + $platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) + ); + + if ($platform->supportsForeignKeyConstraints()) { + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table); + } + } + } + $sql = array_merge($sql, $foreignKeySql); + + if ($saveMode === false) { + foreach ($this->removedTables as $table) { + $sql[] = $platform->getDropTableSQL($table); + } + } + + foreach ($this->changedTables as $tableDiff) { + $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php new file mode 100644 index 00000000..a8cb93d7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,126 @@ +getName()." requires a named foreign key, ". + "but the given foreign key from (".implode(", ", $foreignKey->getColumns()).") onto foreign table ". + "'".$foreignKey->getForeignTableName()."' (".implode(", ", $foreignKey->getForeignColumns()).") is currently ". + "unnamed." + ); + } + + static public function alterTableChangeNotSupported($changeName) { + return new self ("Alter table change not supported, given '$changeName'"); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php new file mode 100644 index 00000000..87ff0fa7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence Structure + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var int + */ + protected $_allocationSize = 1; + + /** + * @var int + */ + protected $_initialValue = 1; + + /** + * + * @param string $name + * @param int $allocationSize + * @param int $initialValue + */ + public function __construct($name, $allocationSize=1, $initialValue=1) + { + $this->_setName($name); + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + public function getAllocationSize() + { + return $this->_allocationSize; + } + + public function getInitialValue() + { + return $this->_initialValue; + } + + public function setAllocationSize($allocationSize) + { + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + } + + public function setInitialValue($initialValue) + { + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + /** + * Check if this sequence is an autoincrement sequence for a given table. + * + * This is used inside the comparator to not report sequences as missing, + * when the "from" schema implicitly creates the sequences. + * + * @param Table $table + * + * @return bool + */ + public function isAutoIncrementsFor(Table $table) + { + if ( ! $table->hasPrimaryKey()) { + return false; + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + if (count($pkColumns) != 1) { + return false; + } + + $column = $table->getColumn($pkColumns[0]); + + if ( ! $column->getAutoincrement()) { + return false; + } + + $sequenceName = $this->getShortestName($table->getNamespaceName()); + $tableName = $table->getShortestName($table->getNamespaceName()); + $tableSequenceName = sprintf('%s_%s_seq', $tableName, $pkColumns[0]); + + return $tableSequenceName === $sequenceName; + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSequence($this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php new file mode 100644 index 00000000..9a4f2ec1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,371 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; + +/** + * SqliteSchemaManager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @author Martin Hasoň + * @version $Revision$ + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * @override + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + * + * @override + */ + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $driver = $params['driver']; + $options = array( + 'driver' => $driver, + 'path' => $database + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($options); + $conn->connect(); + $conn->close(); + } + + /** + * {@inheritdoc} + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->fromTable = $this->listTableDetails($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->addedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->changedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropForeignKey($foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->removedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function listTableForeignKeys($table, $database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + if ( ! empty($tableForeignKeys)) { + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + if (preg_match_all('# + (?:CONSTRAINT\s+([^\s]+)\s+)? + (?:FOREIGN\s+KEY[^\)]+\)\s*)? + REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))? + (?: + [^,]*? + (NOT\s+DEFERRABLE|DEFERRABLE) + (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? + )?#isx', + $createSql, $match)) { + + $names = array_reverse($match[1]); + $deferrable = array_reverse($match[2]); + $deferred = array_reverse($match[3]); + } else { + $names = $deferrable = $deferred = array(); + } + + foreach ($tableForeignKeys as $key => $value) { + $id = $value['id']; + $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id; + $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false; + $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false; + } + } + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery( "PRAGMA TABLE_INFO ('$tableName')" ); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + foreach($indexArray as $indexColumnRow) { + if($indexColumnRow['pk'] == "1") { + $indexBuffer[] = array( + 'key_name' => 'primary', + 'primary' => true, + 'non_unique' => false, + 'column_name' => $indexColumnRow['name'] + ); + } + } + + // fetch regular indexes + foreach($tableIndexes as $tableIndex) { + // Ignore indexes with reserved names, e.g. autoindexes + if (strpos($tableIndex['name'], 'sqlite_') !== 0) { + $keyName = $tableIndex['name']; + $idx = array(); + $idx['key_name'] = $keyName; + $idx['primary'] = false; + $idx['non_unique'] = $tableIndex['unique']?false:true; + + $stmt = $this->_conn->executeQuery( "PRAGMA INDEX_INFO ( '{$keyName}' )" ); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + foreach ( $indexArray as $indexColumnRow ) { + $idx['column_name'] = $indexColumnRow['name']; + $indexBuffer[] = $idx; + } + } + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); + $autoincrementColumn = null; + $autoincrementCount = 0; + foreach ($tableColumns as $tableColumn) { + if ('1' == $tableColumn['pk']) { + $autoincrementCount++; + if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) { + $autoincrementColumn = $tableColumn['name']; + } + } + } + + if (1 == $autoincrementCount && null !== $autoincrementColumn) { + foreach ($list as $column) { + if ($autoincrementColumn == $column->getName()) { + $column->setAutoincrement(true); + } + } + } + + return $list; + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $e = explode('(', $tableColumn['type']); + $tableColumn['type'] = $e[0]; + if (isset($e[1])) { + $length = trim($e[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = (boolean) isset($tableColumn['unsigned']) ? $tableColumn['unsigned'] : false; + $fixed = false; + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $default = $tableColumn['dflt_value']; + if ($default == 'NULL') { + $default = null; + } + if ($default !== null) { + // SQLite returns strings wrapped in single quotes, so we need to strip them + $default = preg_replace("/^'(.*)'$/", '\1', $default); + } + $notnull = (bool) $tableColumn['notnull']; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'decimal': + case 'numeric': + if (isset($tableColumn['length'])) { + if (strpos($tableColumn['length'], ',') === false) { + $tableColumn['length'] .= ",0"; + } + list($precision, $scale) = array_map('trim', explode(',', $tableColumn['length'])); + } + $length = null; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => $fixed, + 'notnull' => $notnull, + 'default' => $default, + 'precision' => $precision, + 'scale' => $scale, + 'autoincrement' => false, + ); + + return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $value) { + $value = array_change_key_case($value, CASE_LOWER); + $name = $value['constraint_name']; + if ( ! isset($list[$name])) { + if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") { + $value['on_delete'] = null; + } + if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") { + $value['on_update'] = null; + } + + $list[$name] = array( + 'name' => $name, + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['table'], + 'onDelete' => $value['on_delete'], + 'onUpdate' => $value['on_update'], + 'deferrable' => $value['deferrable'], + 'deferred'=> $value['deferred'], + ); + } + $list[$name]['local'][] = $value['from']; + $list[$name]['foreign'][] = $value['to']; + } + + $result = array(); + foreach($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + 'deferrable' => $constraint['deferrable'], + 'deferred'=> $constraint['deferred'], + ) + ); + } + + return $result; + } + + private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + if ( ! $table instanceof Table) { + $tableDetails = $this->tryMethod('listTableDetails', $table); + if (false === $table) { + throw new \DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table)); + } + + $table = $tableDetails; + } + + $tableDiff = new TableDiff($table->getName()); + $tableDiff->fromTable = $table; + + return $tableDiff; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php new file mode 100644 index 00000000..16fb0339 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; + +/** + * Abstract schema synchronizer with methods for executing batches of SQL. + */ +abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer +{ + /** + * @var Connection + */ + protected $conn; + + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + protected function processSqlSafely(array $sql) + { + foreach ($sql as $s) { + try { + $this->conn->exec($s); + } catch(\Exception $e) { + + } + } + } + + protected function processSql(array $sql) + { + foreach ($sql as $s) { + $this->conn->exec($s); + } + } + +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php new file mode 100644 index 00000000..74f26438 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Schema\Schema; + +/** + * The synchronizer knows how to synchronize a schema with the configured + * database. + * + * @author Benjamin Eberlei + */ +interface SchemaSynchronizer +{ + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + function getCreateSchema(Schema $createSchema); + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + function getUpdateSchema(Schema $toSchema, $noDrops = false); + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + function getDropSchema(Schema $dropSchema); + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + function getDropAllSchema(); + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + function createSchema(Schema $createSchema); + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return void + */ + function updateSchema(Schema $toSchema, $noDrops = false); + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + function dropSchema(Schema $dropSchema); + + /** + * Drop all assets from the underlying db. + * + * @return void + */ + function dropAllSchema(); +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php new file mode 100644 index 00000000..5501fc7d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -0,0 +1,197 @@ +. + */ +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; + +/** + * Schema Synchronizer for Default DBAL Connection + * + * @author Benjamin Eberlei + */ +class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + public function __construct(Connection $conn) + { + parent::__construct($conn); + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + public function getCreateSchema(Schema $createSchema) + { + return $createSchema->toSql($this->platform); + } + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + $comparator = new Comparator(); + $sm = $this->conn->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($noDrops) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + public function getDropSchema(Schema $dropSchema) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $sm = $this->conn->getSchemaManager(); + + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ( $dropSchema->hasTable($table->getName())) { + $visitor->acceptTable($table); + } + + foreach ($table->getForeignKeys() as $foreignKey) { + if ( ! $dropSchema->hasTable($table->getName())) { + continue; + } + + if ( ! $dropSchema->hasTable($foreignKey->getForeignTableName())) { + continue; + } + + $visitor->acceptForeignKey($table, $foreignKey); + } + } + + if ( ! $this->platform->supportsSequences()) { + return $visitor->getQueries(); + } + + foreach ($dropSchema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($dropSchema->getTables() as $table) { + /* @var $sequence Table */ + if ( ! $table->hasPrimaryKey()) { + continue; + } + + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) > 1) { + continue; + } + + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + + return $visitor->getQueries(); + } + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + public function getDropAllSchema() + { + $sm = $this->conn->getSchemaManager(); + $visitor = new DropSchemaSqlCollector($this->platform); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $sm->createSchema(); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return void + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * Drop all assets from the underlying db. + * + * @return void + */ + public function dropAllSchema() + { + $this->processSql($this->getDropAllSchema()); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php new file mode 100644 index 00000000..893a6fe9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,680 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\DBALException; + +/** + * Object Representation of a table + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var array + */ + protected $_columns = array(); + + /** + * @var array + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var array + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * + * @param string $tableName + * @param array $columns + * @param array $indexes + * @param array $fkConstraints + * @param int $idGeneratorType + * @param array $options + */ + public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array()) + { + if (strlen($tableName) == 0) { + throw DBALException::invalidTableName($tableName); + } + + $this->_setName($tableName); + $this->_idGeneratorType = $idGeneratorType; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + + foreach ($indexes as $idx) { + $this->_addIndex($idx); + } + + foreach ($fkConstraints as $constraint) { + $this->_addForeignKeyConstraint($constraint); + } + + $this->_options = $options; + } + + /** + * @param SchemaConfig $schemaConfig + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return int + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Set Primary Key + * + * @param array $columns + * @param string $indexName + * @return Table + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $primaryKey = $this->_createIndex($columns, $indexName ?: "primary", true, true); + + foreach ($columns as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $primaryKey; + } + + /** + * @param array $columnNames + * @param string $indexName + * @param array $flags + * @return Table + */ + public function addIndex(array $columnNames, $indexName = null, array $flags = array()) + { + if($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, false, false, $flags); + } + + /** + * Drop an index from this table. + * + * @param string $indexName + * @return void + */ + public function dropPrimaryKey() + { + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = false; + } + + /** + * Drop an index from this table. + * + * @param string $indexName + * @return void + */ + public function dropIndex($indexName) + { + $indexName = strtolower($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + unset($this->_indexes[$indexName]); + } + + /** + * + * @param array $columnNames + * @param string $indexName + * @return Table + */ + public function addUniqueIndex(array $columnNames, $indexName = null) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, true, false); + } + + /** + * Check if an index begins in the order of the given columns. + * + * @param array $columnsNames + * @return bool + */ + public function columnsAreIndexed(array $columnsNames) + { + foreach ($this->getIndexes() as $index) { + /* @var $index Index */ + if ($index->spansColumns($columnsNames)) { + return true; + } + } + return false; + } + + /** + * + * @param array $columnNames + * @param string $indexName + * @param bool $isUnique + * @param bool $isPrimary + * @param array $flags + * @return Table + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array()) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + $this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags)); + return $this; + } + + /** + * @param string $columnName + * @param string $columnType + * @param array $options + * @return Column + */ + public function addColumn($columnName, $typeName, array $options=array()) + { + $column = new Column($columnName, Type::getType($typeName), $options); + + $this->_addColumn($column); + return $column; + } + + /** + * Rename Column + * + * @param string $oldColumnName + * @param string $newColumnName + * @return Table + */ + public function renameColumn($oldColumnName, $newColumnName) + { + throw new DBALException("Table#renameColumn() was removed, because it drops and recreates " . + "the column instead. There is no fix available, because a schema diff cannot reliably detect if a " . + "column was renamed or one column was created and another one dropped."); + } + + /** + * Change Column Details + * + * @param string $columnName + * @param array $options + * @return Table + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + return $this; + } + + /** + * Drop Column from Table + * + * @param string $columnName + * @return Table + */ + public function dropColumn($columnName) + { + $columnName = strtolower($columnName); + $column = $this->getColumn($columnName); + unset($this->_columns[$columnName]); + return $this; + } + + + /** + * Add a foreign key constraint + * + * Name is inferred from the local columns + * + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @param string $constraintName + * @return Table + */ + public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array(), $constraintName = null) + { + $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array)$this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength()); + return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Add a foreign key constraint + * + * Name is to be generated by the database itself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Add a foreign key constraint with a given name + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param string $name + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + $foreignTableName = $foreignTable->getName(); + + foreach ($foreignColumnNames as $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } else { + $foreignTableName = $foreignTable; + } + + foreach ($localColumnNames as $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * @return Table + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + return $this; + } + + /** + * @param Column $column + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = strtolower($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Add index to table + * + * @param Index $indexCandidate + * @return Table + */ + protected function _addIndex(Index $indexCandidate) + { + // check for duplicates + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $indexName = $indexCandidate->getName(); + $indexName = strtolower($indexName); + + if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $indexCandidate->isPrimary())) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + // remove overruled indexes + foreach ($this->_indexes as $idxKey => $existingIndex) { + if ($indexCandidate->overrules($existingIndex)) { + unset($this->_indexes[$idxKey]); + } + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + if(strlen($constraint->getName())) { + $name = $constraint->getName(); + } else { + $name = $this->_generateIdentifierName( + array_merge((array)$this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength() + ); + } + $name = strtolower($name); + + $this->_fkConstraints[$name] = $constraint; + // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request. + // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes + // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns). + $this->addIndex($constraint->getColumns()); + } + + /** + * Does Table have a foreign key constraint with the given name? + * * + * @param string $constraintName + * @return bool + */ + public function hasForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * @param string $constraintName + * @return ForeignKeyConstraint + */ + public function getForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + if(!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + public function removeForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + if(!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + unset($this->_fkConstraints[$constraintName]); + } + + /** + * @return Column[] + */ + public function getColumns() + { + $columns = $this->_columns; + + $pkCols = array(); + $fkCols = array(); + + if ($this->hasPrimaryKey()) { + $pkCols = $this->getPrimaryKey()->getColumns(); + } + foreach ($this->getForeignKeys() as $fk) { + /* @var $fk ForeignKeyConstraint */ + $fkCols = array_merge($fkCols, $fk->getColumns()); + } + $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns))); + + uksort($columns, function($a, $b) use($colNames) { + return (array_search($a, $colNames) >= array_search($b, $colNames)); + }); + return $columns; + } + + + /** + * Does this table have a column with the given name? + * + * @param string $columnName + * @return bool + */ + public function hasColumn($columnName) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + return isset($this->_columns[$columnName]); + } + + /** + * Get a column instance + * + * @param string $columnName + * @return Column + */ + public function getColumn($columnName) + { + $columnName = strtolower($this->trimQuotes($columnName)); + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * @return Index|null + */ + public function getPrimaryKey() + { + if ( ! $this->hasPrimaryKey()) { + return null; + } + return $this->getIndex($this->_primaryKeyName); + } + + public function getPrimaryKeyColumns() + { + if ( ! $this->hasPrimaryKey()) { + throw new DBALException("Table " . $this->getName() . " has no primary key."); + } + return $this->getPrimaryKey()->getColumns(); + } + + /** + * Check if this table has a primary key. + * + * @return bool + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * @param string $indexName + * @return bool + */ + public function hasIndex($indexName) + { + $indexName = strtolower($indexName); + return (isset($this->_indexes[$indexName])); + } + + /** + * @param string $indexName + * @return Index + */ + public function getIndex($indexName) + { + $indexName = strtolower($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + return $this->_indexes[$indexName]; + } + + /** + * @return array + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Get Constraints + * + * @return array + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptTable($this); + + foreach ($this->getColumns() as $column) { + $visitor->acceptColumn($this, $column); + } + + foreach ($this->getIndexes() as $index) { + $visitor->acceptIndex($this, $index); + } + + foreach ($this->getForeignKeys() as $constraint) { + $visitor->acceptForeignKey($this, $constraint); + } + } + + /** + * Clone of a Table triggers a deep clone of all affected assets + */ + public function __clone() + { + foreach ($this->_columns as $k => $column) { + $this->_columns[$k] = clone $column; + } + foreach ($this->_indexes as $k => $index) { + $this->_indexes[$k] = clone $index; + } + foreach ($this->_fkConstraints as $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; + $this->_fkConstraints[$k]->setLocalTable($this); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php new file mode 100644 index 00000000..1f5d1daf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,143 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Table Diff + * + * + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @author Benjamin Eberlei + */ +class TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string + */ + public $newName = false; + + /** + * All added fields + * + * @var array(string=>Column) + */ + public $addedColumns; + + /** + * All changed fields + * + * @var array(string=>Column) + */ + public $changedColumns = array(); + + /** + * All removed fields + * + * @var array(string=>Column) + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var array(string=>Column) + */ + public $renamedColumns = array(); + + /** + * All added indexes + * + * @var array(string=>Index) + */ + public $addedIndexes = array(); + + /** + * All changed indexes + * + * @var array(string=>Index) + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var array(string=>bool) + */ + public $removedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var array + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var array + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var array + */ + public $removedForeignKeys = array(); + + /** + * @var Table + */ + public $fromTable; + + /** + * Constructs an TableDiff object. + * + * @param array(string=>Column) $addedColumns + * @param array(string=>Column) $changedColumns + * @param array(string=>bool) $removedColumns + * @param array(string=>Index) $addedIndexes + * @param array(string=>Index) $changedIndexes + * @param array(string=>bool) $removedIndexes + * @param Table $fromTable + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array(), Table $fromTable = null) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + $this->fromTable = $fromTable; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php new file mode 100644 index 00000000..8283d07d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php @@ -0,0 +1,53 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +/** + * Representation of a Database View + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_sql; + + public function __construct($name, $sql) + { + $this->_setName($name); + $this->_sql = $sql; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php new file mode 100644 index 00000000..ba1d1ee4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Abstract Visitor with empty methods for easy extension. + */ +class AbstractVisitor implements Visitor +{ + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php new file mode 100644 index 00000000..56bce9a1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,157 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence; + +class CreateSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var array + */ + private $createTableQueries = array(); + + /** + * @var array + */ + private $createSequenceQueries = array(); + + /** + * @var array + */ + private $createFkConstraintQueries = array(); + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform = null; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + } + + /** + * Generate DDL Statements to create the accepted table with all its dependencies. + * + * @param Table $table + */ + public function acceptTable(Table $table) + { + $namespace = $this->getNamespace($table); + + $this->createTableQueries[$namespace] = array_merge( + $this->createTableQueries[$namespace], + $this->platform->getCreateTableSQL($table) + ); + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $namespace = $this->getNamespace($localTable); + + if ($this->platform->supportsForeignKeyConstraints()) { + $this->createFkConstraintQueries[$namespace] = array_merge( + $this->createFkConstraintQueries[$namespace], + (array) $this->platform->getCreateForeignKeySQL( + $fkConstraint, $localTable + ) + ); + } + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + $namespace = $this->getNamespace($sequence); + + $this->createSequenceQueries[$namespace] = array_merge( + $this->createSequenceQueries[$namespace], + (array)$this->platform->getCreateSequenceSQL($sequence) + ); + } + + private function getNamespace($asset) + { + $namespace = $asset->getNamespaceName() ?: 'default'; + if ( !isset($this->createTableQueries[$namespace])) { + $this->createTableQueries[$namespace] = array(); + $this->createSequenceQueries[$namespace] = array(); + $this->createFkConstraintQueries[$namespace] = array(); + } + + return $namespace; + } + + /** + * @return array + */ + public function resetQueries() + { + $this->createTableQueries = array(); + $this->createSequenceQueries = array(); + $this->createFkConstraintQueries = array(); + } + + /** + * Get all queries collected so far. + * + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach (array_keys($this->createTableQueries) as $namespace) { + if ($this->platform->supportsSchemas()) { + // TODO: Create Schema here + } + } + + foreach ($this->createTableQueries as $schemaSql) { + $sql = array_merge($sql, $schemaSql); + } + + foreach ($this->createSequenceQueries as $schemaSql) { + $sql = array_merge($sql, $schemaSql); + } + + foreach ($this->createFkConstraintQueries as $schemaSql) { + $sql = array_merge($sql, $schemaSql); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php new file mode 100644 index 00000000..37085c6e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\SchemaException, + Doctrine\DBAL\Schema\Index; + +/** + * Gather SQL statements that allow to completely drop the current schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint); + $this->constraints[$fkConstraint] = $localTable; + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + $this->sequences->attach($sequence); + } + + /** + * @return void + */ + public function clearQueries() + { + $this->constraints = new \SplObjectStorage(); + $this->sequences = new \SplObjectStorage(); + $this->tables = new \SplObjectStorage(); + } + + /** + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach ($this->constraints as $fkConstraint) { + $localTable = $this->constraints[$fkConstraint]; + $sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable); + } + + foreach ($this->sequences as $sequence) { + $sql[] = $this->platform->getDropSequenceSQL($sequence); + } + + foreach ($this->tables as $table) { + $sql[] = $this->platform->getDropTableSQL($table); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php new file mode 100644 index 00000000..08c5a292 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint; + +/** + * Create a Graphviz output of a Schema. + */ +class Graphviz extends AbstractVisitor +{ + /** + * @var string + */ + private $output = ''; + + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $this->output .= $this->createNodeRelation( + $fkConstraint->getLocalTableName() . ":col" . current($fkConstraint->getLocalColumns()).":se", + $fkConstraint->getForeignTableName() . ":col" . current($fkConstraint->getForeignColumns()).":se", + array( + 'dir' => 'back', + 'arrowtail' => 'dot', + 'arrowhead' => 'normal', + ) + ); + } + + public function acceptSchema(Schema $schema) + { + $this->output = 'digraph "' . sha1( mt_rand() ) . '" {' . "\n"; + $this->output .= 'splines = true;' . "\n"; + $this->output .= 'overlap = false;' . "\n"; + $this->output .= 'outputorder=edgesfirst;'."\n"; + $this->output .= 'mindist = 0.6;' . "\n"; + $this->output .= 'sep = .2;' . "\n"; + } + + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel( $table ), + 'shape' => 'plaintext', + ) + ); + } + + private function createTableLabel( Table $table ) + { + // Start the table + $label = '<'; + + // The title + $label .= ''; + + // The attributes block + foreach( $table->getColumns() as $column ) { + $columnLabel = $column->getName(); + + $label .= ''; + $label .= ''; + $label .= ''; + } + + // End the table + $label .= '
' . $table->getName() . '
'; + $label .= '' . $columnLabel . ''; + $label .= '' . strtolower($column->getType()) . ''; + if ($table->hasPrimaryKey() && in_array($column->getName(), $table->getPrimaryKey()->getColumns())) { + $label .= "\xe2\x9c\xb7"; + } + $label .= '
>'; + + return $label; + } + + private function createNode( $name, $options ) + { + $node = $name . " ["; + foreach( $options as $key => $value ) + { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + return $node; + } + + private function createNodeRelation( $node1, $node2, $options ) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach( $options as $key => $value ) + { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + return $relation; + } + + /** + * Get Graphviz Output + * + * @return string + */ + public function getOutput() + { + return $this->output . "}"; + } + + /** + * Write dot language output to a file. This should usually be a *.dot file. + * + * You have to convert the output into a viewable format. For example use "neato" on linux systems + * and execute: + * + * neato -Tpng -o er.png er.dot + * + * @param string $filename + * @return void + */ + public function write($filename) + { + file_put_contents($filename, $this->getOutput()); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php new file mode 100644 index 00000000..dd6becfd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence; + +/** + * Remove assets from a schema that are not in the default namespace. + * + * Some databases such as MySQL support cross databases joins, but don't + * allow to call DDLs to a database from another connected database. + * Before a schema is serialized into SQL this visitor can cleanup schemas with + * non default namespaces. + * + * This visitor filters all these non-default namespaced tables and sequences + * and removes them from the SChema instance. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class RemoveNamespacedAssets extends AbstractVisitor +{ + /** + * @var Schema + */ + private $schema; + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropTable($table->getName()); + } + } + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + // The table may already be deleted in a previous + // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that + // point to nowhere. + if ( ! $this->schema->hasTable($fkConstraint->getForeignTableName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + return; + } + + $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); + if ( ! $foreignTable->isInDefaultNamespace($this->schema->getName()) ) { + $localTable->removeForeignKey($fkConstraint->getName()); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php new file mode 100644 index 00000000..3c921dab --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\SchemaException, + Doctrine\DBAL\Schema\Index; + +/** + * Visit a SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Benjamin Eberlei + */ +interface SchemaDiffVisitor +{ + /** + * Visit an orphaned foreign key whose table was deleted. + * + * @param ForeignKeyConstraint $foreignKey + */ + function visitOrphanedForeignKey(ForeignKeyConstraint $foreignKey); + + /** + * Visit a sequence that has changed. + * + * @param Sequence $sequence + */ + function visitChangedSequence(Sequence $sequence); + + /** + * Visit a sequence that has been removed. + * + * @param Sequence $sequence + */ + function visitRemovedSequence(Sequence $sequence); + + function visitNewSequence(Sequence $sequence); + + function visitNewTable(Table $table); + function visitNewTableForeignKey(Table $table, ForeignKeyConstraint $foreignKey); + function visitRemovedTable(Table $table); + function visitChangedTable(TableDiff $tableDiff); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php new file mode 100644 index 00000000..7ee62d3e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema); + + /** + * @param Table $table + */ + public function acceptTable(Table $table); + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php new file mode 100644 index 00000000..3496e6de --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -0,0 +1,201 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; + +use Doctrine\Common\EventManager; + +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Sharding implementation that pools many different connections + * internally and serves data from the currently active connection. + * + * The internals of this class are: + * + * - All sharding clients are specified and given a shard-id during + * configuration. + * - By default, the global shard is selected. If no global shard is configured + * an exception is thrown on access. + * - Selecting a shard by distribution value delegates the mapping + * "distributionValue" => "client" to the ShardChooser interface. + * - An exception is thrown if trying to switch shards during an open + * transaction. + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection', + * 'driver' => 'pdo_mysql', + * 'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'shards' => array( + * array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ), + * 'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser', + * )); + * $shardManager = $conn->getShardManager(); + * $shardManager->selectGlobal(); + * $shardManager->selectShard($value); + * + * @author Benjamin Eberlei + */ +class PoolingShardConnection extends Connection +{ + /** + * @var array + */ + private $activeConnections; + + /** + * @var int + */ + private $activeShardId; + + /** + * @var array + */ + private $connections; + + /** + * @var ShardManager + */ + private $shardManager; + + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['global']) || !isset($params['shards'])) { + throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations."); + } + + if ( !isset($params['shardChoser'])) { + throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'"); + } + + if (is_string($params['shardChoser'])) { + $params['shardChoser'] = new $params['shardChoser']; + } + + if ( ! ($params['shardChoser'] instanceof ShardChoser)) { + throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser"); + } + + $this->connections[0] = array_merge($params, $params['global']); + + foreach ($params['shards'] as $shard) { + if ( ! isset($shard['id'])) { + throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specify a unique shard-id."); + } + + if ( !is_numeric($shard['id']) || $shard['id'] < 1) { + throw new \InvalidArgumentException("Shard Id has to be a non-negative number."); + } + + if (isset($this->connections[$shard['id']])) { + throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration."); + } + + $this->connections[$shard['id']] = array_merge($params, $shard); + } + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Connect to a given shard + * + * @param mixed $shardId + * @return bool + */ + public function connect($shardId = null) + { + if ($shardId === null && $this->_conn) { + return false; + } + + if ($shardId !== null && $shardId === $this->activeShardId) { + return false; + } + + if ($this->getTransactionNestingLevel() > 0) { + throw new ShardingException("Cannot switch shard when transaction is active."); + } + + $this->activeShardId = (int)$shardId; + + if (isset($this->activeConnections[$this->activeShardId])) { + $this->_conn = $this->activeConnections[$this->activeShardId]; + return false; + } + + $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId); + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new \Doctrine\DBAL\Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + + /** + * Connect to a specific connection + * + * @param string $shardId + * @return Driver + */ + protected function connectTo($shardId) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->connections[$shardId]; + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + public function isConnected($shardId = null) + { + if ($shardId === null) { + return $this->_conn !== null; + } + + return isset($this->activeConnections[$shardId]); + } + + public function close() + { + $this->_conn = null; + $this->activeConnections = null; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php new file mode 100644 index 00000000..6f6c5d2c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Shard Manager for the Connection Pooling Shard Strategy + * + * @author Benjamin Eberlei + */ +class PoolingShardManager implements ShardManager +{ + private $conn; + private $choser; + private $currentDistributionValue; + + public function __construct(PoolingShardConnection $conn) + { + $params = $conn->getParams(); + $this->conn = $conn; + $this->choser = $params['shardChoser']; + } + + public function selectGlobal() + { + $this->conn->connect(0); + $this->currentDistributionValue = null; + } + + public function selectShard($distributionValue) + { + $shardId = $this->choser->pickShard($distributionValue, $this->conn); + $this->conn->connect($shardId); + $this->currentDistributionValue = $distributionValue; + } + + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + public function getShards() + { + $params = $this->conn->getParams(); + $shards = array(); + + foreach ($params['shards'] as $shard) { + $shards[] = array('id' => $shard['id']); + } + + return $shards; + } + + public function queryAll($sql, array $params, array $types) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found."); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->selectShard($shard['id']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php new file mode 100644 index 00000000..8f42f2a8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -0,0 +1,297 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer; + +/** + * SQL Azure Schema Synchronizer + * + * Will iterate over all shards when performing schema operations. This is done + * by partitioning the passed schema into subschemas for the federation and the + * global database and then applying the operations step by step using the + * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}. + * + * @author Benjamin Eberlei + */ +class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer +{ + const FEDERATION_TABLE_FEDERATED = 'azure.federated'; + const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName'; + + + /** + * @var SQLAzureShardManager + */ + private $shardManager; + + /** + * @var SchemaSynchronizer + */ + private $synchronizer; + + public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null) + { + parent::__construct($conn); + $this->shardManager = $shardManager; + $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); + } + + /** + * Get the SQL statements that can be executed to create the schema. + * + * @param Schema $createSchema + * @return array + */ + public function getCreateSchema(Schema $createSchema) + { + $sql = array(); + + list($global, $federation) = $this->partitionSchema($createSchema); + + $globalSql = $this->synchronizer->getCreateSchema($global); + if ($globalSql) { + $sql[] = "-- Create Root Federation\n" . + "USE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $federationSql = $this->synchronizer->getCreateSchema($federation); + + if ($federationSql) { + $defaultValue = $this->getFederationTypeDefaultValue(); + + $sql[] = $this->getCreateFederationStatement(); + $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + + return $sql; + } + + /** + * Get the SQL Statements to update given schema with the underlying db. + * + * @param Schema $toSchema + * @param bool $noDrops + * @return array + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + return $this->work($toSchema, function($synchronizer, $schema) use ($noDrops) { + return $synchronizer->getUpdateSchema($schema, $noDrops); + }); + } + + /** + * Get the SQL Statements to drop the given schema from underlying db. + * + * @param Schema $dropSchema + * @return array + */ + public function getDropSchema(Schema $dropSchema) + { + return $this->work($dropSchema, function($synchronizer, $schema) { + return $synchronizer->getDropSchema($schema); + }); + } + + /** + * Create the Schema + * + * @param Schema $createSchema + * @return void + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * Update the Schema to new schema version. + * + * @param Schema $toSchema + * @return void + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * Drop the given database schema from the underlying db. + * + * @param Schema $dropSchema + * @return void + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * Get the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + public function getDropAllSchema() + { + $this->shardManager->selectGlobal(); + $globalSql = $this->synchronizer->getDropAllSchema(); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $this->synchronizer->getDropAllSchema(); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + $sql[] = "USE FEDERATION ROOT WITH RESET;"; + $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName(); + + return $sql; + } + + /** + * Drop all assets from the underlying db. + * + * @return void + */ + public function dropAllSchema() + { + $this->processSqlSafely($this->getDropAllSchema()); + } + + private function partitionSchema(Schema $schema) + { + return array( + $this->extractSchemaFederation($schema, false), + $this->extractSchemaFederation($schema, true), + ); + } + + private function extractSchemaFederation(Schema $schema, $isFederation) + { + $partitionedSchema = clone $schema; + + foreach ($partitionedSchema->getTables() as $table) { + if ($isFederation) { + $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); + } + + if ( $table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + $partitionedSchema->dropTable($table->getName()); + } else { + foreach ($table->getForeignKeys() as $fk) { + $foreignTable = $schema->getTable($fk->getForeignTableName()); + if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + throw new \RuntimeException("Cannot have foreign key between global/federation."); + } + } + } + } + + return $partitionedSchema; + } + + /** + * Work on the Global/Federation based on currently existing shards and + * perform the given operation on the underlying schema synchronizer given + * the different partitioned schema instances. + * + * @param Schema $schema + * @param Closure $operation + * @return array + */ + private function work(Schema $schema, \Closure $operation) + { + list($global, $federation) = $this->partitionSchema($schema); + $sql = array(); + + $this->shardManager->selectGlobal(); + $globalSql = $operation($this->synchronizer, $global); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $operation($this->synchronizer, $federation); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + return $sql; + } + + private function getFederationTypeDefaultValue() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + + switch ($federationType->getName()) { + case Type::GUID: + $defaultValue = '00000000-0000-0000-0000-000000000000'; + break; + case Type::INTEGER: + case Type::SMALLINT: + case Type::BIGINT: + $defaultValue = '0'; + break; + default: + $defaultValue = ''; + break; + } + return $defaultValue; + } + + private function getCreateFederationStatement() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + $federationTypeSql = $federationType->getSqlDeclaration(array(), $this->conn->getDatabasePlatform()); + + return "--Create Federation\n" . + "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ." RANGE)"; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php new file mode 100644 index 00000000..80ca3d92 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -0,0 +1,238 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Sharding\ShardManager; +use Doctrine\DBAL\Sharding\ShardingException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Sharding using the SQL Azure Federations support. + * + * @author Benjamin Eberlei + */ +class SQLAzureShardManager implements ShardManager +{ + /** + * @var string + */ + private $federationName; + + /** + * @var bool + */ + private $filteringEnabled; + + /** + * @var string + */ + private $distributionKey; + + /** + * @var string + */ + private $distributionType; + + /** + * @var Connection + */ + private $conn; + + /** + * @var string + */ + private $currentDistributionValue; + + /** + * @param Connection $conn + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + $params = $conn->getParams(); + + if ( ! isset($params['sharding']['federationName'])) { + throw ShardingException::missingDefaultFederationName(); + } + + if ( ! isset($params['sharding']['distributionKey'])) { + throw ShardingException::missingDefaultDistributionKey(); + } + + if ( ! isset($params['sharding']['distributionType'])) { + throw ShardingException::missingDistributionType(); + } + + $this->federationName = $params['sharding']['federationName']; + $this->distributionKey = $params['sharding']['distributionKey']; + $this->distributionType = $params['sharding']['distributionType']; + $this->filteringEnabled = (isset($params['sharding']['filteringEnabled'])) ? (bool)$params['sharding']['filteringEnabled'] : false; + } + + /** + * Get name of the federation + * + * @return string + */ + public function getFederationName() + { + return $this->federationName; + } + + /** + * Get the distribution key + * + * @return string + */ + public function getDistributionKey() + { + return $this->distributionKey; + } + + /** + * Get the Doctrine Type name used for the distribution + * + * @return string + */ + public function getDistributionType() + { + return $this->distributionType; + } + + /** + * Enabled/Disable filtering on the fly. + * + * @param bool $flag + * @return void + */ + public function setFilteringEnabled($flag) + { + $this->filteringEnabled = (bool)$flag; + } + + /** + * {@inheritDoc} + */ + public function selectGlobal() + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + $sql = "USE FEDERATION ROOT WITH RESET"; + $this->conn->exec($sql); + $this->currentDistributionValue = null; + } + + /** + * {@inheritDoc} + */ + public function selectShard($distributionValue) + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + if ($distributionValue === null || is_bool($distributionValue) || !is_scalar($distributionValue)) { + throw ShardingException::noShardDistributionValue(); + } + + $platform = $this->conn->getDatabasePlatform(); + $sql = sprintf( + "USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;", + $platform->quoteIdentifier($this->federationName), + $platform->quoteIdentifier($this->distributionKey), + $this->conn->quote($distributionValue), + ($this->filteringEnabled ? 'ON' : 'OFF') + ); + + $this->conn->exec($sql); + $this->currentDistributionValue = $distributionValue; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * {@inheritDoc} + */ + public function getShards() + { + $sql = "SELECT member_id as id, + distribution_name as distribution_key, + CAST(range_low AS CHAR) AS rangeLow, + CAST(range_high AS CHAR) AS rangeHigh + FROM sys.federation_member_distributions d + INNER JOIN sys.federations f ON f.federation_id = d.federation_id + WHERE f.name = " . $this->conn->quote($this->federationName); + return $this->conn->fetchAll($sql); + } + + /** + * {@inheritDoc} + */ + public function queryAll($sql, array $params = array(), array $types = array()) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found for " . $this->federationName); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->selectShard($shard['rangeLow']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } + + /** + * Split Federation at a given distribution value. + * + * @param mixed $splitDistributionValue + */ + public function splitFederation($splitDistributionValue) + { + $type = Type::getType($this->distributionType); + + $sql = "ALTER FEDERATION " . $this->getFederationName() . " " . + "SPLIT AT (" . $this->getDistributionKey() . " = " . + $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ")"; + $this->conn->exec($sql); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php new file mode 100644 index 00000000..2b2b4578 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Converts a single tenant schema into a multi-tenant schema for SQL Azure + * Federations under the following assumptions: + * + * - Every table is part of the multi-tenant application, only explicitly + * excluded tables are non-federated. The behavior of the tables being in + * global or federated database is undefined. It depends on you selecting a + * federation before DDL statements or not. + * - Every Primary key of a federated table is extended by another column + * 'tenant_id' with a default value of the SQLAzure function + * `federation_filtering_value('tenant_id')`. + * - You always have to work with `filtering=On` when using federations with this + * multi-tenant approach. + * - Primary keys are either using globally unique ids (GUID, Table Generator) + * or you explicitly add the tenent_id in every UPDATE or DELETE statement + * (otherwise they will affect the same-id rows from other tenents as well). + * SQLAzure throws errors when you try to create IDENTIY columns on federated + * tables. + * + * @author Benjamin Eberlei + */ +class MultiTenantVisitor implements Visitor +{ + /** + * @var array + */ + private $excludedTables = array(); + + /** + * @var string + */ + private $tenantColumnName; + + /** + * @var string + */ + private $tenantColumnType = 'integer'; + + /** + * Name of the federation distribution, defaulting to the tenantColumnName + * if not specified. + * + * @var string + */ + private $distributionName; + + public function __construct(array $excludedTables = array(), $tenantColumnName = 'tenant_id', $distributionName = null) + { + $this->excludedTables = $excludedTables; + $this->tenantColumnName = $tenantColumnName; + $this->distributionName = $distributionName ?: $tenantColumnName; + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + if (in_array($table->getName(), $this->excludedTables)) { + return; + } + + $table->addColumn($this->tenantColumnName, $this->tenantColumnType, array( + 'default' => "federation_filtering_value('". $this->distributionName ."')", + )); + + $clusteredIndex = $this->getClusteredIndex($table); + + $indexColumns = $clusteredIndex->getColumns(); + $indexColumns[] = $this->tenantColumnName; + + if ($clusteredIndex->isPrimary()) { + $table->dropPrimaryKey(); + $table->setPrimaryKey($indexColumns); + } else { + $table->dropIndex($clusteredIndex->getName()); + $table->addIndex($indexColumns, $clusteredIndex->getName()); + $table->getIndex($clusteredIndex->getName())->addFlag('clustered'); + } + } + + private function getClusteredIndex($table) + { + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { + return $index; + } else if ($index->hasFlag('clustered')) { + return $index; + } + } + throw new \RuntimeException("No clustered index found on table " . $table->getName()); + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php new file mode 100644 index 00000000..c6cdabfb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * The MultiTenant Shard choser assumes that the distribution value directly + * maps to the shard id. + * + * @author Benjamin Eberlei + */ +class MultiTenantShardChoser implements ShardChoser +{ + public function pickShard($distributionValue, PoolingShardConnection $conn) + { + return $distributionValue; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php new file mode 100644 index 00000000..2aa9f74e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * Given a distribution value this shard-choser strategy will pick the shard to + * connect to for retrieving rows with the distribution value. + * + * @author Benjamin Eberlei + */ +interface ShardChoser +{ + /** + * Pick a shard for the given distribution value + * + * @param string $distributionValue + * @param PoolingShardConnection $conn + * @return int + */ + function pickShard($distributionValue, PoolingShardConnection $conn); +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php new file mode 100644 index 00000000..f962150f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Connection; + +/** + * Sharding Manager gives access to APIs to implementing sharding on top of + * Doctrine\DBAL\Connection instances. + * + * For simplicity and developer ease-of-use (and understanding) the sharding + * API only covers single shard queries, no fan-out support. It is primarily + * suited for multi-tenant applications. + * + * The assumption about sharding here + * is that a distribution value can be found that gives access to all the + * necessary data for all use-cases. Switching between shards should be done with + * caution, especially if lazy loading is implemented. Any query is always + * executed against the last shard that was selected. If a query is created for + * a shard Y but then a shard X is selected when its actually executed you + * will hit the wrong shard. + * + * @author Benjamin Eberlei + */ +interface ShardManager +{ + /** + * Select global database with global data. + * + * This is the default database that is connected when no shard is + * selected. + * + * @return void + */ + function selectGlobal(); + + /** + * SELECT queries after this statement will be issued against the selected + * shard. + * + * @throws ShardingException If no value is passed as shard identifier. + * @param mixed $distributionValue + * @param array $options + * @return void + */ + function selectShard($distributionValue); + + /** + * Get the distribution value currently used for sharding. + * + * @return string + */ + function getCurrentDistributionValue(); + + /** + * Get information about the amount of shards and other details. + * + * Format is implementation specific, each shard is one element and has an + * 'id' attribute at least. + * + * @return array + */ + function getShards(); + + /** + * Query all shards in undefined order and return the results appended to + * each other. Restore the previous distribution value after execution. + * + * Using {@link Connection::fetchAll} to retrieve rows internally. + * + * @param string $sql + * @param array $params + * @param array $types + * @return array + */ + function queryAll($sql, array $params, array $types); +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php new file mode 100644 index 00000000..06dd1695 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\DBALException; + +/** + * Sharding related Exceptions + * + * @since 2.3 + */ +class ShardingException extends DBALException +{ + static public function notImplemented() + { + return new self("This functionality is not implemented with this sharding provider.", 1331557937); + } + + static public function missingDefaultFederationName() + { + return new self("SQLAzure requires a federation name to be set during sharding configuration.", 1332141280); + } + + static public function missingDefaultDistributionKey() + { + return new self("SQLAzure requires a distribution key to be set during sharding configuration.", 1332141329); + } + + static public function activeTransaction() + { + return new self("Cannot switch shard during an active transaction.", 1332141766); + } + + static public function noShardDistributionValue() + { + return new self("You have to specify a string or integer as shard distribution value.", 1332142103); + } + + static public function missingDistributionType() + { + return new self("You have to specify a sharding distribution type such as 'integer', 'string', 'guid'."); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php new file mode 100644 index 00000000..ce917185 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php @@ -0,0 +1,268 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Driver\Statement as DriverStatement; + +/** + * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support + * for logging, DBAL mapping types, etc. + * + * @author Roman Borschel + * @since 2.0 + */ +class Statement implements \IteratorAggregate, DriverStatement +{ + /** + * @var string The SQL statement. + */ + protected $sql; + /** + * @var array The bound parameters. + */ + protected $params = array(); + /** + * @var array The parameter types + */ + protected $types = array(); + /** + * @var \Doctrine\DBAL\Driver\Statement The underlying driver statement. + */ + protected $stmt; + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform The underlying database platform. + */ + protected $platform; + /** + * @var \Doctrine\DBAL\Connection The connection this statement is bound to and executed on. + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param \Doctrine\DBAL\Connection The connection on which the statement should be executed. + */ + public function __construct($sql, Connection $conn) + { + $this->sql = $sql; + $this->stmt = $conn->getWrappedConnection()->prepare($sql); + $this->conn = $conn; + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Binds a parameter value to the statement. + * + * The value can optionally be bound with a PDO binding type or a DBAL mapping type. + * If bound with a DBAL mapping type, the binding type is derived from the mapping + * type and the value undergoes the conversion routines of the mapping type before + * being bound. + * + * @param string $name The name or position of the parameter. + * @param mixed $value The value of the parameter. + * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindValue($name, $value, $type = null) + { + $this->params[$name] = $value; + $this->types[$name] = $type; + if ($type !== null) { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + return $this->stmt->bindValue($name, $value, $bindingType); + } else { + return $this->stmt->bindValue($name, $value); + } + } + + /** + * Binds a parameter to a value by reference. + * + * Binding a parameter by reference does not support DBAL mapping types. + * + * @param string $name The name or position of the parameter. + * @param mixed $var The reference to the variable to bind + * @param integer $type The PDO binding type. + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindParam($name, &$var, $type = PDO::PARAM_STR, $length = null) + { + return $this->stmt->bindParam($name, $var, $type, $length ); + } + + /** + * Executes the statement with the currently bound parameters. + * + * @param array $params + * @return boolean TRUE on success, FALSE on failure. + */ + public function execute($params = null) + { + if (is_array($params)) { + $this->params = $params; + } + + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + $stmt = $this->stmt->execute($params); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($ex, $this->sql, $this->conn->resolveParams($this->params, $this->types)); + } + + if ($logger) { + $logger->stopQuery(); + } + $this->params = array(); + $this->types = array(); + return $stmt; + } + + /** + * Closes the cursor, freeing the database resources used by this statement. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * Returns the number of columns in the result set. + * + * @return integer + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * Fetches the SQLSTATE associated with the last operation on the statement. + * + * @return string + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * Fetches extended error information associated with the last operation on the statement. + * + * @return array + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 === null) { + return $this->stmt->setFetchMode($fetchMode); + } else if ($arg3 === null) { + return $this->stmt->setFetchMode($fetchMode, $arg2); + } + + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + public function getIterator() + { + return $this->stmt; + } + + /** + * Fetches the next row from a result set. + * + * @param integer $fetchMode + * @return mixed The return value of this function on success depends on the fetch type. + * In all cases, FALSE is returned on failure. + */ + public function fetch($fetchMode = null) + { + return $this->stmt->fetch($fetchMode); + } + + /** + * Returns an array containing all of the result set rows. + * + * @param integer $fetchMode + * @param mixed $fetchArgument + * @return array An array containing all of the remaining rows in the result set. + */ + public function fetchAll($fetchMode = null, $fetchArgument = 0) + { + if ($fetchArgument !== 0) { + return $this->stmt->fetchAll($fetchMode, $fetchArgument); + } + return $this->stmt->fetchAll($fetchMode); + } + + /** + * Returns a single column from the next row of a result set. + * + * @param integer $columnIndex + * @return mixed A single column from the next row of a result set or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->stmt->fetchColumn($columnIndex); + } + + /** + * Returns the number of rows affected by the last execution of this statement. + * + * @return integer The number of affected rows. + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } + + /** + * Gets the wrapped driver statement. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + public function getWrappedStatement() + { + return $this->stmt; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php new file mode 100644 index 00000000..6b2b8c27 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ImportCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:import') + ->setDescription('Import SQL file(s) directly to Database.') + ->setDefinition(array( + new InputArgument( + 'file', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'File path(s) of SQL to be executed.' + ) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($fileNames = $input->getArgument('file')) !== null) { + foreach ((array) $fileNames as $fileName) { + $fileName = realpath($fileName); + + if ( ! file_exists($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $fileName) + ); + } else if ( ! is_readable($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $fileName) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $fileName)); + $sql = file_get_contents($fileName); + + if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // PDO Drivers + try { + $lines = 0; + + $stmt = $conn->prepare($sql); + $stmt->execute(); + + do { + // Required due to "MySQL has gone away!" issue + $stmt->fetch(); + $stmt->closeCursor(); + + $lines++; + } while ($stmt->nextRowset()); + + $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); + } catch (\PDOException $e) { + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } else { + // Non-PDO Drivers (ie. OCI8 driver) + $stmt = $conn->prepare($sql); + $rs = $stmt->execute(); + + if ($rs) { + $output->writeln('OK!' . PHP_EOL); + } else { + $error = $stmt->errorInfo(); + + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($error[2], $error[0]); + } + + $stmt->closeCursor(); + } + } + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php new file mode 100644 index 00000000..36184066 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,150 @@ +. + */ + + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Command\Command, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'sqlserver' => 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords', + 'sqlserver2005' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords', + 'sqlserver2008' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords', + 'sqlserver2012' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command + * + * @param string $name + * @param string $class + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:reserved-words') + ->setDescription('Checks if the current database contains identifiers that are reserved.') + ->setDefinition(array( + new InputOption( + 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Keyword-List name.' + ) + )) + ->setHelp(<<%command.full_name% + +If you want to check against specific dialects you can +pass them to the command: + + %command.full_name% mysql pgsql + +The following keyword lists are currently shipped with Doctrine: + + * mysql + * pgsql + * sqlite + * oracle + * sqlserver + * sqlserver2005 + * sqlserver2008 + * sqlserver2012 + * db2 (Not checked by default) +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $conn \Doctrine\DBAL\Connection */ + $conn = $this->getHelper('db')->getConnection(); + + $keywordLists = (array)$input->getOption('list'); + if ( ! $keywordLists) { + $keywordLists = array( + 'mysql', + 'pgsql', + 'sqlite', + 'oracle', + 'sqlserver', + 'sqlserver2005', + 'sqlserver2008', + 'sqlserver2012' + ); + } + + $keywords = array(); + foreach ($keywordLists as $keywordList) { + if (!isset($this->keywordListClasses[$keywordList])) { + throw new \InvalidArgumentException( + "There exists no keyword list with name '" . $keywordList . "'. ". + "Known lists: " . implode(", ", array_keys($this->keywordListClasses)) + ); + } + $class = $this->keywordListClasses[$keywordList]; + $keywords[] = new $class; + } + + $output->write('Checking keyword violations for ' . implode(", ", $keywordLists) . "...", true); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $conn->getSchemaManager()->createSchema(); + $visitor = new ReservedKeywordsValidator($keywords); + $schema->visit($visitor); + + $violations = $visitor->getViolations(); + if (count($violations) == 0) { + $output->write("No reserved keywords violations have been found!", true); + } else { + $output->write('There are ' . count($violations) . ' reserved keyword violations in your database schema:', true); + foreach ($violations as $violation) { + $output->write(' - ' . $violation, true); + } + + return 1; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php new file mode 100644 index 00000000..b1af34bd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunSqlCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:run-sql') + ->setDescription('Executes arbitrary SQL directly from the command line.') + ->setDefinition(array( + new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), + new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', 7) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($sql = $input->getArgument('sql')) === null) { + throw new \RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + if (stripos($sql, 'select') === 0) { + $resultSet = $conn->fetchAll($sql); + } else { + $resultSet = $conn->executeUpdate($sql); + } + + ob_start(); + \Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth); + $message = ob_get_clean(); + + $output->write($message); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php new file mode 100644 index 00000000..877cb64f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper, + Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * Doctrine Database Connection + * @var Connection + */ + protected $_connection; + + /** + * Constructor + * + * @param Connection $connection Doctrine Database Connection + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves Doctrine Database Connection + * + * @return Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @see Helper + */ + public function getName() + { + return 'connection'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php new file mode 100644 index 00000000..4017a1da --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP array to a clob SQL type. + * + * @since 2.0 + */ +class ArrayType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + // @todo 3.0 - $value === null check to save real NULL in database + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value != 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + + public function getName() + { + return Type::TARRAY; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php new file mode 100644 index 00000000..7648bef6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database BIGINT to a PHP string. + * + * @author robo + * @since 2.0 + */ +class BigIntType extends Type +{ + public function getName() + { + return Type::BIGINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (string) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php new file mode 100644 index 00000000..ff046556 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL BLOB to a PHP resource stream + * + * @since 2.2 + */ +class BlobType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $value = fopen('data://text/plain;base64,' . base64_encode($value), 'r'); + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + + return $value; + } + + public function getName() + { + return Type::BLOB; + } + + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php new file mode 100644 index 00000000..f1a968d7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL boolean to a PHP boolean. + * + * @since 2.0 + */ +class BooleanType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleans($value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (bool) $value; + } + + public function getName() + { + return Type::BOOLEAN; + } + + public function getBindingType() + { + return \PDO::PARAM_BOOL; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php new file mode 100644 index 00000000..3a19d1a4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,65 @@ +. + */ + + +/** + * Conversion Exception is thrown when the database to PHP conversion fails + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +namespace Doctrine\DBAL\Types; + +class ConversionException extends \Doctrine\DBAL\DBALException +{ + /** + * Thrown when a Database to Doctrine Type Conversion fails. + * + * @param string $value + * @param string $toType + * @return ConversionException + */ + static public function conversionFailed($value, $toType) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType); + } + + /** + * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement + * about the expected format. + * + * @param string $value + * @param string $toType + * @return ConversionException + */ + static public function conversionFailedFormat($value, $toType, $expectedFormat) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + return new self( + 'Could not convert database value "' . $value . '" to Doctrine Type ' . + $toType . '. Expected format: ' . $expectedFormat + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php new file mode 100644 index 00000000..06de729b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object. + * + * @since 2.0 + */ +class DateTimeType extends Type +{ + public function getName() + { + return Type::DATETIME; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + } + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php new file mode 100644 index 00000000..e0a786c6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,79 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * DateTime type saving additional timezone information. + * + * Caution: Databases are not necessarily experts at storing timezone related + * data of dates. First, of all the supported vendors only PostgreSQL and Oracle + * support storing Timezone data. But those two don't save the actual timezone + * attached to a DateTime instance (for example "Europe/Berlin" or "America/Montreal") + * but the current offset of them related to UTC. That means depending on daylight saving times + * or not you may get different offsets. + * + * This datatype makes only sense to use, if your application works with an offset, not + * with an actual timezone that uses transitions. Otherwise your DateTime instance + * attached with a timezone such as Europe/Berlin gets saved into the database with + * the offset and re-created from persistence with only the offset, not the original timezone + * attached. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + public function getName() + { + return Type::DATETIMETZ; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeTzFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeTzFormatString()); + } + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php new file mode 100644 index 00000000..a3f70181 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATE to a PHP Date object. + * + * @since 2.0 + */ +class DateType extends Type +{ + public function getName() + { + return Type::DATE; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat('!'.$platform->getDateFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateFormatString()); + } + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php new file mode 100644 index 00000000..fd61b53f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP double. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + public function getName() + { + return Type::DECIMAL; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php new file mode 100644 index 00000000..d02ca0c5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,54 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + public function getName() + { + return Type::FLOAT; + } + + /** + * @param array $fieldDeclaration + * @param AbstractPlatform $platform + * @return string + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (double) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php new file mode 100644 index 00000000..8722f027 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Represents a GUID/UUID datatype (both are actually synonyms) in the database. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class GuidType extends StringType +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + } + + public function getName() + { + return Type::GUID; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return !$platform->hasNativeGuidType(); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php new file mode 100644 index 00000000..bac9b3f5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL INT to a PHP integer. + * + * @author Roman Borschel + * @since 2.0 + */ +class IntegerType extends Type +{ + public function getName() + { + return Type::INTEGER; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php new file mode 100644 index 00000000..ca005578 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used to generate json arrays. + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class JsonArrayType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + return json_encode($value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return json_decode($value, true); + } + + public function getName() + { + return Type::JSON_ARRAY; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php new file mode 100644 index 00000000..9510d29c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP object to a clob SQL type. + * + * @since 2.0 + */ +class ObjectType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value !== 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + + public function getName() + { + return Type::OBJECT; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php new file mode 100644 index 00000000..719d7f1e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used for simple values. + * + * Only use this type if you are sure that your values cannot contain a ",". + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class SimpleArrayType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!$value) { + return null; + } + + return implode(',', $value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return explode(',', $value); + } + + public function getName() + { + return Type::SIMPLE_ARRAY; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php new file mode 100644 index 00000000..97e9aaff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database SMALLINT to a PHP integer. + * + * @author robo + */ +class SmallIntType extends Type +{ + public function getName() + { + return Type::SMALLINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php new file mode 100644 index 00000000..48c76d6a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL VARCHAR to a PHP string. + * + * @since 2.0 + */ +class StringType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** @override */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** @override */ + public function getName() + { + return Type::STRING; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php new file mode 100644 index 00000000..98ecbe62 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL CLOB to a PHP string. + * + * @since 2.0 + */ +class TextType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + public function getName() + { + return Type::TEXT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php new file mode 100644 index 00000000..8653750f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL TIME to a PHP DateTime object. + * + * @since 2.0 + */ +class TimeType extends Type +{ + public function getName() + { + return Type::TIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getTimeFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getTimeFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getTimeFormatString()); + } + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php new file mode 100644 index 00000000..29947bef --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\DBALException; + +/** + * The base class for so-called Doctrine mapping types. + * + * A Type object is obtained by calling the static {@link getType()} method. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class Type +{ + const TARRAY = 'array'; + const SIMPLE_ARRAY = 'simple_array'; + const JSON_ARRAY = 'json_array'; + const BIGINT = 'bigint'; + const BOOLEAN = 'boolean'; + const DATETIME = 'datetime'; + const DATETIMETZ = 'datetimetz'; + const DATE = 'date'; + const TIME = 'time'; + const DECIMAL = 'decimal'; + const INTEGER = 'integer'; + const OBJECT = 'object'; + const SMALLINT = 'smallint'; + const STRING = 'string'; + const TEXT = 'text'; + const BLOB = 'blob'; + const FLOAT = 'float'; + const GUID = 'guid'; + + /** Map of already instantiated type objects. One instance per type (flyweight). */ + private static $_typeObjects = array(); + + /** The map of supported doctrine mapping types. */ + private static $_typesMap = array( + self::TARRAY => 'Doctrine\DBAL\Types\ArrayType', + self::SIMPLE_ARRAY => 'Doctrine\DBAL\Types\SimpleArrayType', + self::JSON_ARRAY => 'Doctrine\DBAL\Types\JsonArrayType', + self::OBJECT => 'Doctrine\DBAL\Types\ObjectType', + self::BOOLEAN => 'Doctrine\DBAL\Types\BooleanType', + self::INTEGER => 'Doctrine\DBAL\Types\IntegerType', + self::SMALLINT => 'Doctrine\DBAL\Types\SmallIntType', + self::BIGINT => 'Doctrine\DBAL\Types\BigIntType', + self::STRING => 'Doctrine\DBAL\Types\StringType', + self::TEXT => 'Doctrine\DBAL\Types\TextType', + self::DATETIME => 'Doctrine\DBAL\Types\DateTimeType', + self::DATETIMETZ => 'Doctrine\DBAL\Types\DateTimeTzType', + self::DATE => 'Doctrine\DBAL\Types\DateType', + self::TIME => 'Doctrine\DBAL\Types\TimeType', + self::DECIMAL => 'Doctrine\DBAL\Types\DecimalType', + self::FLOAT => 'Doctrine\DBAL\Types\FloatType', + self::BLOB => 'Doctrine\DBAL\Types\BlobType', + self::GUID => 'Doctrine\DBAL\Types\GuidType', + ); + + /* Prevent instantiation and force use of the factory method. */ + final private function __construct() {} + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Gets the default length of this type. + * + * @todo Needed? + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return null; + } + + /** + * Gets the SQL declaration snippet for a field of this type. + * + * @param array $fieldDeclaration The field declaration. + * @param AbstractPlatform $platform The currently used database platform. + */ + abstract public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); + + /** + * Gets the name of this type. + * + * @return string + * @todo Needed? + */ + abstract public function getName(); + + /** + * Factory method to create type instances. + * Type instances are implemented as flyweights. + * + * @static + * @throws DBALException + * @param string $name The name of the type (as returned by getName()). + * @return \Doctrine\DBAL\Types\Type + */ + public static function getType($name) + { + if ( ! isset(self::$_typeObjects[$name])) { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::unknownColumnType($name); + } + self::$_typeObjects[$name] = new self::$_typesMap[$name](); + } + + return self::$_typeObjects[$name]; + } + + /** + * Adds a custom type to the type map. + * + * @static + * @param string $name Name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * @throws DBALException + */ + public static function addType($name, $className) + { + if (isset(self::$_typesMap[$name])) { + throw DBALException::typeExists($name); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Checks if exists support for a type. + * + * @static + * @param string $name Name of the type + * @return boolean TRUE if type is supported; FALSE otherwise + */ + public static function hasType($name) + { + return isset(self::$_typesMap[$name]); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @static + * @param string $name + * @param string $className + * @throws DBALException + */ + public static function overrideType($name, $className) + { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::typeNotFound($name); + } + + if (isset(self::$_typeObjects[$name])) { + unset(self::$_typeObjects[$name]); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Gets the (preferred) binding type for values of this type that + * can be used when binding parameters to prepared statements. + * + * This method should return one of the PDO::PARAM_* constants, that is, one of: + * + * PDO::PARAM_BOOL + * PDO::PARAM_NULL + * PDO::PARAM_INT + * PDO::PARAM_STR + * PDO::PARAM_LOB + * + * @return integer + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * Get the types array map which holds all registered types and the corresponding + * type class + * + * @return array $typesMap + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + public function __toString() + { + $e = explode('\\', get_class($this)); + return str_replace('Type', '', end($e)); + } + + /** + * Does working with this column require SQL conversion functions? + * + * This is a metadata function that is required for example in the ORM. + * Usage of {@link convertToDatabaseValueSQL} and + * {@link convertToPHPValueSQL} works for any type and mostly + * does nothing. This method can additionally be used for optimization purposes. + * + * @return bool + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param AbstractPlatform $platform + * @return string + */ + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return $sqlExpr; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. + * + * @param string $sqlExpr + * @param AbstractPlatform $platform + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Get an array of database types that map to this Doctrine type. + * + * @param AbstractPlatform $platform + * @return array + */ + public function getMappedDatabaseTypes(AbstractPlatform $platform) + { + return array(); + } + + /** + * If this Doctrine Type maps to an already mapped database type, + * reverse schema engineering can't take them apart. You need to mark + * one of those types as commented, which will have Doctrine use an SQL + * comment to typehint the actual Doctrine Type. + * + * @param AbstractPlatform $platform + * @return bool + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return false; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php new file mode 100644 index 00000000..70858223 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,60 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Variable DateTime Type using date_create() instead of DateTime::createFromFormat() + * + * This type has performance implications as it runs twice as long as the regular + * {@see DateTimeType}, however in certain PostgreSQL configurations with + * TIMESTAMP(n) columns where n > 0 it is necessary to use this type. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * @throws ConversionException + * @param string $value + * @param AbstractPlatform $platform + * @return \DateTime + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = date_create($value); + if ( ! $val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php new file mode 100644 index 00000000..3afb1443 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Class to store and retrieve the version of Doctrine + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.4.0-RC1'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * @return int Returns -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/doctrine-module/.gitignore b/vendor/doctrine/doctrine-module/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/vendor/doctrine/doctrine-module/.travis.yml b/vendor/doctrine/doctrine-module/.travis.yml new file mode 100644 index 00000000..b8744083 --- /dev/null +++ b/vendor/doctrine/doctrine-module/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - cd .. + - git clone git://github.com/zendframework/ZendSkeletonApplication.git + - cd ZendSkeletonApplication + - cp ../DoctrineModule/.travis/composer.json ./ + - curl -s http://getcomposer.org/installer | php + - php composer.phar install --dev --prefer-source + - rm -rf vendor/doctrine/doctrine-module + - cp -r ../DoctrineModule vendor/doctrine/doctrine-module + - rm config/application.config.php && cp ../DoctrineModule/.travis/application.config.php config/ + +script: + - ./vendor/bin/doctrine-module help + - ./vendor/bin/doctrine-module list + - cd vendor/doctrine/doctrine-module/tests + - phpunit diff --git a/vendor/doctrine/doctrine-module/.travis/application.config.php b/vendor/doctrine/doctrine-module/.travis/application.config.php new file mode 100644 index 00000000..5a5e0db5 --- /dev/null +++ b/vendor/doctrine/doctrine-module/.travis/application.config.php @@ -0,0 +1,23 @@ + array( + 'Application', + 'DoctrineModule', + ), + 'module_listener_options' => array( + 'config_glob_paths' => array( + 'config/autoload/{,*.}{global,local}.php', + ), + 'config_cache_enabled' => false, + 'cache_dir' => 'data/cache', + 'module_paths' => array( + './module', + './vendor', + ), + ), + 'service_manager' => array( + 'use_defaults' => true, + 'factories' => array( + ), + ), +); diff --git a/vendor/doctrine/doctrine-module/.travis/composer.json b/vendor/doctrine/doctrine-module/.travis/composer.json new file mode 100644 index 00000000..e8245f86 --- /dev/null +++ b/vendor/doctrine/doctrine-module/.travis/composer.json @@ -0,0 +1,6 @@ +{ + "minimum-stability": "dev", + "require": { + "doctrine/doctrine-module": "dev-master" + } +} diff --git a/vendor/doctrine/doctrine-module/LICENSE b/vendor/doctrine/doctrine-module/LICENSE new file mode 100644 index 00000000..516f6816 --- /dev/null +++ b/vendor/doctrine/doctrine-module/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2006-2012 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. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/Module.php b/vendor/doctrine/doctrine-module/Module.php new file mode 100644 index 00000000..d668c294 --- /dev/null +++ b/vendor/doctrine/doctrine-module/Module.php @@ -0,0 +1,7 @@ +. + */ + +use Zend\ServiceManager\ServiceManager; +use Zend\Mvc\Application; + +ini_set('display_errors', true); +chdir(__DIR__); + +$previousDir = '.'; + +while (!file_exists('config/application.config.php')) { + $dir = dirname(getcwd()); + + if ($previousDir === $dir) { + throw new RuntimeException( + 'Unable to locate "config/application.config.php": ' . + 'is DoctrineModule in a subdir of your application skeleton?' + ); + } + + $previousDir = $dir; + chdir($dir); +} + +if (!(@include_once __DIR__ . '/../vendor/autoload.php') && !(@include_once __DIR__ . '/../../../autoload.php')) { + throw new RuntimeException('Error: vendor/autoload.php could not be found. Did you run php composer.phar install?'); +} + +$application = Application::init(include 'config/application.config.php'); + +/* @var $cli \Symfony\Component\Console\Application */ +$cli = $application->getServiceManager()->get('doctrine.cli'); +$cli->run(); diff --git a/vendor/doctrine/doctrine-module/composer.json b/vendor/doctrine/doctrine-module/composer.json new file mode 100644 index 00000000..3468c7d8 --- /dev/null +++ b/vendor/doctrine/doctrine-module/composer.json @@ -0,0 +1,68 @@ +{ + "name": "doctrine/doctrine-module", + "description": "Zend Framework 2 Module that provides Doctrine basic functionality required for ORM and ODM modules", + "type": "library", + "license": "MIT", + "keywords": [ + "doctrine", + "module", + "zf2" + ], + "homepage": "http://www.doctrine-project.org/", + "authors": [ + { + "name": "Kyle Spraggs", + "email": "theman@spiffyjr.me", + "homepage": "http://www.spiffyjr.me/" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://marco-pivetta.com/" + }, + { + "name": "Evan Coury", + "email": "me@evancoury.com", + "homepage": "http://blog.evan.pro/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Michaël Gallego", + "email": "mic.gallego@gmail.com", + "homepage": "http://www.michaelgallego.fr" + } + ], + "require": { + "php": ">=5.3.3", + "doctrine/common": ">=2.3-dev,<2.5-dev", + "symfony/console": ">=2.0.13", + "zendframework/zend-authentication": "2.*", + "zendframework/zend-cache": "2.*", + "zendframework/zend-paginator": "2.*", + "zendframework/zend-stdlib": "2.*", + "zendframework/zend-mvc": "2.*", + "zendframework/zend-servicemanager": "2.*", + "zendframework/zend-validator": "2.*" + }, + "suggest": { + "doctrine/data-fixtures": "Data Fixtures if you want to generate test data or bootstrap data for your deployments" + }, + "minimum-stability": "dev", + "autoload": { + "psr-0": { + "DoctrineModule\\": "src/", + "DoctrineModuleTest\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.8.x-dev" + } + }, + "bin": [ + "bin/doctrine-module" + ] +} diff --git a/vendor/doctrine/doctrine-module/config/module.config.php b/vendor/doctrine/doctrine-module/config/module.config.php new file mode 100644 index 00000000..9eb1c398 --- /dev/null +++ b/vendor/doctrine/doctrine-module/config/module.config.php @@ -0,0 +1,56 @@ +. + */ + +return array( + 'doctrine' => array( + 'cache' => array( + 'apc' => array( + 'class' => 'Doctrine\Common\Cache\ApcCache', + ), + 'array' => array( + 'class' => 'Doctrine\Common\Cache\ArrayCache', + ), + 'filesystem' => array( + 'class' => 'Doctrine\Common\Cache\FilesystemCache', + 'directory' => 'data/DoctrineModule/cache', + ), + 'memcache' => array( + 'class' => 'Doctrine\Common\Cache\MemcacheCache', + 'instance' => 'my_memcache_alias', + ), + 'memcached' => array( + 'class' => 'Doctrine\Common\Cache\MemcachedCache', + 'instance' => 'my_memcached_alias', + ), + 'redis' => array( + 'class' => 'Doctrine\Common\Cache\RedisCache', + 'instance' => 'my_redis_alias', + ), + 'wincache' => array( + 'class' => 'Doctrine\Common\Cache\WinCacheCache', + ), + 'xcache' => array( + 'class' => 'Doctrine\Common\Cache\XcacheCache', + ), + 'zenddata' => array( + 'class' => 'Doctrine\Common\Cache\ZendDataCache', + ), + ), + ), +); diff --git a/vendor/doctrine/doctrine-module/docs/authentication.md b/vendor/doctrine/doctrine-module/docs/authentication.md new file mode 100644 index 00000000..d941d3aa --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/authentication.md @@ -0,0 +1,179 @@ +# Authentication + +Authentication through Doctrine is fully supported by DoctrineModule through an authentication adapter, and a specific storage implementation that relies on the database. Most of the time, those classes will be used in conjunction with `Zend\Authentication\AuthenticationService` class. + +### Simple example + +In order to authenticate a user (or anything else) against Doctrine, the following workflow will be use : + +1. Create an authentication adapter that contains options about the entity that is authenticated (credential property, identity property…). +2. Create a storage adapter. If the authentication succeeds, the identifier of the entity will be automatically stored in session. +3. Create a `Zend\Authentication\AuthenticationService`instance that contains both the authentication adapter and the storage adapter. + +#### Authentication factory + +To make your life easier, DoctrineModule provides an Authentication factory through the ``DoctrineModule\Options\Authentication`` class. + +The first task is to configure the Authentication by adding the ``authentication`` key to the ``doctrine`` key in your config file (we assume here that the entity we want to authentication is simply called `Application\Entity\User`): + +```php +// in your module.config.php: + +return array( + 'doctrine' => array( + 'authentication' => array( + 'orm_default' => array( + 'object_manager' => 'Doctrine\ORM\EntityManager', + 'identity_class' => 'Application\Entity\User', + 'identity_property' => 'email', + 'credential_property' => 'password', + ), + ), + ), +); +``` + +Here are some explanations about the keys: + +* the `object_manager` key can either be a concrete instance of a `Doctrine\Common\Persistence\ObjectManager` or a single string that will fetched from the Service Manager in order to get a concrete instance. If you are using DoctrineORMModule, you can simply write 'Doctrine\ORM\EntityManager' (as the EntityManager implements the class `Doctrine\Common\Persistence\ObjectManager`). +* the `identity_class` contains the FQCN of the entity that will be used during the authentication process. +* the `identity_property` contains the name of the property that will be used as the identity property (most often, this is email, username…). Please note that we are talking here of the PROPERTY, not the table column name (although it can be the same in most of the cases). +* the `credential_property` contains the name of the property that will be used as the credential property (most often, this is password…). + +The authentication accept some more options that can be used : + +* the `object_repository` can be used instead of the `object_manager` key. Most of the time you won't deal with the one, as specifying the `identity_class` name will automatically fetch the `object_repository` for you. +* the `credential_callable` is a very useful option that allow you to perform some custom logic when checking if the credential is correct. For instance, if your password are encrypted using Bcrypt algorithm, you will need to perform specific logic. This option can be any callable function (closure, class method…). This function will be given the complete entity fetched from the database, and the credential that was given by the user during the authentication process. + +Here is an example code that adds the `credential_callable` function to our previous example : + +```php +// in your module.config.php: + +return array( + 'doctrine' => array( + 'authentication' => array( + 'orm_default => array( + 'object_manager' => 'Doctrine\ORM\EntityManager', + 'identity_class' => 'Application\Entity\User', + 'identity_property' => 'email', + 'credential_property' => 'password', + 'credential_callable' => function(User $user, $passwordGiven) { + return my_awesome_check_test($user->getPassword(), $passwordGiven); + }, + ), + ), + ), +); +``` + +#### Creating the AuthenticationService + +Now that we have configured the authentication, we need still need to tell Zend Framework how to construct a correct ``Zend\Authentication\AuthenticationService`` instance. For this, add the following code in your Module.php class: + +```php +namespace Application; + +use Zend\Authentication\AuthenticationService; + +class Module +{ + public function getServiceConfig() + { + return array( + 'factories' => array( + 'Zend\Authentication\AuthenticationService' => function($serviceManager) { + // If you are using DoctrineORMModule: + return $serviceManager->get('doctrine.authenticationservice.orm_default'); + + // If you are using DoctrineODMModule: + return $serviceManager->get('doctrine.authenticationservice.odm_default'); + } + ) + ); + } +} +``` + +Please note that Iam using here a ``Zend\Authentication\AuthenticationService`` name, but it can be anything else (``my_auth_service``…). However, using the name ``Zend\Authentication\AuthenticationService`` will allow it to be recognised by the ZF2 view helper. + +#### Using the AuthenticationService + +Now that we have defined how to create a `Zend\Authentication\AuthenticationService` object, we can use it in our code. For more information about Zend authentication mechanisms, please read [the ZF 2 Authentication's documentation](http://framework.zend.com/manual/2.0/en/modules/zend.authentication.intro.html). + +Here is an example of you we could use it from a controller action (we stripped any Form things for simplicity): + +```php +public function loginAction() +{ + $data = $this->getRequest()->getPost(); + + // If you used another name for the authentication service, change it here + $authService = $this->getServiceLocator()->get('Zend\Authentication\AuthenticationService'); + + $adapter = $authService->getAdapter(); + $adapter->setIdentityValue($data['login']); + $adapter->setCredentialValue($data['password']); + $authResult = $authService->authenticate(); + + if ($authResult->isValid()) { + return $this->redirect()->toRoute('home'); + } + + return new ViewModel(array( + 'error' => 'Your authentication credentials are not valid', + )); +} +``` + +Of course, doing this in the controller is not the best practice, and you'd better move that kind of logic to a service layer. But this is how it works. + +Note that when the authentication is valid, we first get the identity : + +```php +$identity = $authenticationResult->getIdentity(); +``` + +This will return the full entity (in our case, an `Application\Entity\User` instance). However, storing a full entity in session is not a recommended practice. That's why, when writing the identity : + +```php +$authService->getStorage()->write($identity); +``` + +The storage automatically extracts ONLY the identifier values and only store this in session (this avoid to store in session a serialized entity, which is a bad practice). Later, when you want to retrieve the logged user : + +```php +$authenticationService = $this->serviceLocator()->get('Zend\Authentication\AuthenticationService'); +$loggedUser = $authenticationService->getIdentity(); +``` + +The authentication storage will automatically handle the conversion from saved data to managed entity and the opposite. It will avoid serializing entities since that is a strongly discouraged practice. + +#### View helper and controller helper + +You may also need to know if there is an authenticated user within your other controllers or in views. ZF2 provides a controller plugin and a view helper you may use. + +Here is how you use it in your controller : + +```php +public function testAction() +{ + if ($user = $this->identity()) { + // someone is logged ! + } else { + // not logged in + } +} +``` + +And in your view : + +```php +identity()) { + echo 'Logged in as ' . $this->escapeHtml($user->getUsername()); + } else { + echo 'Not logged in'; + } +?> +``` \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/caching.md b/vendor/doctrine/doctrine-module/docs/caching.md new file mode 100644 index 00000000..76b46a7d --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/caching.md @@ -0,0 +1,24 @@ +# Caching + +DoctrineModule provides bridging between +[`Zend\Cache`](https://github.com/zendframework/zf2/tree/master/library/Zend/Cache) +and [`Doctrine\Common\Cache`](https://github.com/doctrine/common/tree/master/lib/Doctrine/Common/Cache). +This may be useful in case you want to share configured cache instances across doctrine, symfony +and zendframework projects. + +You may use `Zend\Cache` within your doctrine-related projects as following: + +```php +$zendCache = new \Zend\Cache\Storage\Adapter\Memory(); // any storage adapter is OK here +$doctrineCache = new \DoctrineModule\Cache\ZendStorageCache($zendCache); +// now use $doctrineCache as a normal Doctrine\Common\Cache\Cache instance +``` + +You may use `Doctrine\Common\Cache` within your zendframework projects as following: + +```php +$doctrineCache = new \Doctrine\Common\Cache\ArrayCache(); // any doctrine cache is OK here +$adapterOptions = new \Zend\Cache\Storage\Adapter\AdapterOptions(); +$zendCacheStorage = new \DoctrineModule\Cache\DoctrineCacheStorageTest($adapterOptions, $zendCache); +// now use $zendCacheStorage as a normal Zend\Cache\Storage\StorageInterface instance. +``` \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/cli.md b/vendor/doctrine/doctrine-module/docs/cli.md new file mode 100644 index 00000000..e8889d90 --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/cli.md @@ -0,0 +1,46 @@ +# Doctrine CLI +The Doctrine CLI has been pre-configured for you and works as is without any special configuration +required for MongoODM ODM and ORM. It will use your application's configuration for +entities or documents. + +Access the Doctrine command line through + +```sh +./vendor/bin/doctrine-module +``` + +Each command provides a description of itself if called with a `--help` argument. + +## Adding commands to the CLI + +You may add your own CLI commands by just creating new +[Symfony commands](http://symfony.com/doc/current/cookbook/console/console_command.html) +and attaching them to the provided CLI application as following: + + +```php +namespace My; + +use Zend\ModuleManager\Feature\BootstrapListenerInterface; +use Zend\EventManager\EventInterface; + +class Module implements BootstrapListenerInterface; +{ + public function onBootstrap(EventInterface $e) + { + $application = $e->getTarget(); + $events = $application->getEventManager()->getSharedManager(); + + // Attach to helper set event and load the entity manager helper. + $events->attach('doctrine', 'loadCli.post', function(EventInterface $e) { + /* @var $cli \Symfony\Component\Console\Application */ + $cli = $e->getTarget(); + + $cli->addCommand(new \My\Own\Cli\Command()); + }); + } +} +``` + +This is not the suggested way of attaching new commands to the CLI, since ZF2 comes +already with console support. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/form-element.md b/vendor/doctrine/doctrine-module/docs/form-element.md new file mode 100644 index 00000000..07e5fe56 --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/form-element.md @@ -0,0 +1,86 @@ +## Form Elements + +DoctrineModule comes with functionality that can automatically fill the +`ValueOptions` of Select, MultiCheckbox or Radio Form Elements with data from a +`ObjectRepository`. + +### Usage + +Add a `DoctrineModule\Form\Element\ObjectSelect`, +`DoctrineModule\Form\Element\ObjectRadio` or +`DoctrineModule\Form\Element\ObjectMultiCheckbox` to your Form. +For this to work, you need to specify at least an `object_manager`, +the `target_class` to use and a `property` of the class to use as the Label. + +#### Example 1 : simple example +```php + +namespace Module\Form; + +use Zend\Form\Form; +use DoctrineModule\Persistence\ObjectManagerAwareInterface; +use Doctrine\Common\Persistence\ObjectManager; + +class MyForm extends Form implements ObjectManagerAwareInterface +{ + protected $objectManager; + + public function init() + { + $this->add( + array( + 'type' => 'DoctrineModule\Form\Element\ObjectSelect', + 'name' => 'name', + 'options' => array( + 'object_manager' => $this->getObjectManager(), + 'target_class' => 'Module\Entity\SomeEntity', + 'property' => 'property', + ), + ) + ); + } + + public function setObjectManager(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + public function getObjectManager() + { + return $this->objectManager; + } +} +``` + +When the Form gets rendered the `findAll` method of the `ObjectRepository` will +be executed by default. + +### Example 2 : extended version + +If you don't need or want the entire repository you can specify a `find_method` +to use. This method must exist in the repository. The following example executes +the `findBy` method and passes in the specified parameters, but when using +custom repositories you can do even more advanced queries! +Also you can specify a method as a property by setting `is_method` to true. + +```php +$this->add( + array( + 'type' => 'DoctrineModule\Form\Element\ObjectSelect', + 'name' => 'name', + 'options' => array( + 'object_manager' => $this->getObjectManager(), + 'target_class' => 'Module\Entity\User', + 'property' => 'ComposedOfSeveralProperties', + 'is_method' => true, + 'find_method' => array( + 'name' => 'findBy', + 'params' => array( + 'criteria' => array('active' => 1), + 'orderBy' => array('lastname' => 'ASC'), + ), + ), + ), + ) +); +``` \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/hydrator.md b/vendor/doctrine/doctrine-module/docs/hydrator.md new file mode 100644 index 00000000..50702d6e --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/hydrator.md @@ -0,0 +1,1459 @@ +## Hydrator + +Hydrators are simple objects that allow to convert an array of data to an object (this is called "hydrating") and to +convert back an object to an array (this is called "extracting"). Hydrators are mainly used in the context of Forms, +with the new binding functionality of Zend Framework 2, but can also be used for any hydrating/extracting context (for +instance, it can be used in RESTful context). If you are not really comfortable with hydrators, please first +read [Zend Framework hydrator's documentation](http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.html). + + +### Basic usage + +DoctrineModule ships with a very powerful hydrator that allow almost any use-case. + +#### Create a hydrator + +To create a Doctrine Hydrator, you just need two things: an object manager (also called Entity Manager in Doctrine ORM +or Document Manager in Doctrine ODM) and the FQCN of the entity: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($objectManager, 'Application\Entity\User'); +``` + +As you can see, contrary to other standard hydrators shipped in Zend Framework 2, the Doctrine Hydrator works with only +one instance type once its created. This means that if you need to hydrate two entities (let's say +`Application\Entity\BlogPost` and `Application\Entity\PostComment`), you will need to create two different hydrators. +It was made like this by design, because the hydrator heavily rely on entities metadata, and it allows us to do some +performance tweaks as well as to provide more consistency to the hydrator code. However, this should not be a problem +as, most of the time, we only create a hydrator to hydrate/extract one type of entity. + +The hydrator constructor also allows a third parameter, `byValue`, which is true by default. We will come back later +about this distinction, but to be short, it allows the hydrator the change the way it gets/sets data by either +accessing the public API of your entity (getters/setters) or directly get/set data through reflection, hence bypassing +any of your custom logic. + +#### Example 1 : simple entity with no associations + +Let's begin by a simple example: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class City +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\Column(type="string", length=48) + */ + protected $name; + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} +``` + +Now, let's use the Doctrine hydrator : + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\City'); +$city = new City(); +$data = array( + 'name' => 'Paris' +); + +$city = $hydrator->hydrate($data, $city); + +echo $city->getName(); // prints "Paris" + +$dataArray = $hydrator->extract($city); +echo $dataArray['name']; // prints "Paris" +``` + +As you can see from this example, in simple cases, the DoctrineModule hydrator provides nearly no benefits over a +simpler hydrator like "ClassMethods". However, even in those cases, I suggest you to use it, as it performs automatically +conversions between types. For instance, it can converts timestamp to DateTime (which is the type used by Doctrine to +represent dates): + +```php + +namespace Application\Entity; + +use DateTime; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class Appointment +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\Column(type="datetime") + */ + protected $time; + + public function getId() + { + return $this->id; + } + + public function setTime(DateTime $time) + { + $this->time = $time; + } + + public function getTime() + { + return $this->time; + } +} +``` + +Let's use the hydrator: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\Appointment'); +$appointment = new Appointment(); +$data = array( + 'time' => '1357057334' +); + +$appointment = $hydrator->hydrate($data, $appointment); + +echo get_class($city->getTime()); // prints "DateTime" +``` + +As you can see, the hydrator automatically converted the timestamp to a DateTime object during the hydration, hence +allowing us to have a nice API in our entity with correct typehint. + + + + +#### Example 2 : OneToOne/ManyToOne associations + +DoctrineModule hydrator is especially useful when dealing with associations (OneToOne, OneToMany, ManyToOne) and +integrates nicely with the Form/Fieldset logic ([learn more about this here](http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html)). + +Let's take a simple example with a BlogPost and a User entity to illustrate OneToOne association: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class User +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\Column(type="string", length=48) + */ + protected $username; + + /** + * @ORM\Column(type="string") + */ + protected $password; + + public function getId() + { + return $this->id; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } +} +``` + +And the BlogPost entity, with a ManyToOne association: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class BlogPost +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\ManyToOne(targetEntity="Application\Entity\User") + */ + protected $user; + + /** + * @ORM\Column(type="string") + */ + protected $title; + + public function getId() + { + return $this->id; + } + + public function setUser(User $user) + { + $this->user = $user; + } + + public function getUser() + { + return $this->user; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + +There are two use cases that can arise when using OneToOne association: the toOne entity (in the case, the user) may +already exist (which will often be the case with a User and BlogPost example), or it can be created too. The +DoctrineHydrator natively supports both cases. + +##### Existing entity in the association + +When the association's entity already exists, what you need to do is simply giving the identifier of the association: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost'); +$blogPost = new BlogPost(); +$data = array( + 'title' => 'The best blog post in the world !', + 'user' => array( + 'id' => 2 // Written by user 2 + ) +); + +$blogPost = $hydrator->hydrate($data, $blogPost); + +echo $blogPost->getTitle(); // prints "The best blog post in the world !" +echo $blogPost->getUser()->getId(); // prints 2 +``` + +**NOTE** : when using association whose primary key is not compound, you can rewrite the following more succintely: + +```php +$data = array( + 'title' => 'The best blog post in the world !', + 'user' => array( + 'id' => 2 // Written by user 2 + ) +); +``` + +to: + +```php +$data = array( + 'title' => 'The best blog post in the world !', + 'user' => 2 +); +``` + + +##### Non-existing entity in the association + +If the association's entity does not exist, you just need to give the given object: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost'); +$blogPost = new BlogPost(); +$user = new User(); +$user->setUsername('bakura'); +$user->setPassword('p@$$w0rd'); + +$data = array( + 'title' => 'The best blog post in the world !', + 'user' => $user +); + +$blogPost = $hydrator->hydrate($data, $blogPost); + +echo $blogPost->getTitle(); // prints "The best blog post in the world !" +echo $blogPost->getUser()->getId(); // prints 2 +``` + +For this to work, you must also slightly change your mapping, so that Doctrine can persist new entities on +associations (note the cascade options on the OneToMany association): + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class BlogPost +{ + /** .. */ + + /** + * @ORM\ManyToOne(targetEntity="Application\Entity\User", cascade={"persist"}) + */ + protected $user; + + /** … */ +} +``` + + +#### Example 3 : OneToMany association + +DoctrineModule hydrator also handles OneToMany relationships (when use `Zend\Form\Element\Collection` element). Please +refer to the official [Zend Framework 2 documentation](http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html) to learn more about Collection. + +> Note: internally, for a given collection, if an array contains identifiers, the hydrator automatically fetch the +objects through the Doctrine `find` function. However, this may cause problems if one of the value of the collection +is the empty string '' (as the ``find`` will most likely fail). In order to solve this problem, empty string identifiers +are simply ignored during the hydration phase. Therefore, if your database contains an empty string value as primary +key, the hydrator could not work correctly (the simplest way to avoid that is simply to not have an empty string primary +key, which should not happen if you use auto-increment primary keys, anyway). + +Let's take again a simple example: a BlogPost and Tag entities. + +```php + +namespace Application\Entity; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class BlogPost +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\OneToMany(targetEntity="Application\Entity\Tag", mappedBy="blogPost") + */ + protected $tags; + + /** + * Never forget to initialize all your collections ! + */ + public function __construct() + { + $this->tags = new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function addTags(Collection $tags) + { + foreach ($tags as $tag) { + $tag->setBlogPost($this); + $this->tags->add($tag); + } + } + + public function removeTags(Collection $tags) + { + foreach ($tags as $tag) { + $tag->setBlogPost(null); + $this->tags->removeElement($tag); + } + } + + public function getTags() + { + return $this->tags; + } +} +``` + +And the Tag entity: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class Tag +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\ManyToOne(targetEntity="Application\Entity\BlogPost", inversedBy="tags") + */ + protected $blogPost; + + /** + * @ORM\Content(type="string") + */ + protected $name; + + public function getId() + { + return $this->id; + } + + /** + * Allow null to remove association + */ + public function setBlogPost(BlogPost $blogPost = null) + { + $this->blogPost = $blogPost; + } + + public function getBlogPost() + { + return $this->blogPost; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} +``` + +Please note interesting things in BlogPost entity. We have defined two functions: addTags and removeTags. Those +functions must be always defined and are called automatically by Doctrine hydrator when dealing with collections. +You may think this is overkill, and ask why you cannot just define a `setTags` function to replace the old collection +by the new one: + +```php +public function setTags(Collection $tags) +{ + $this->tags = $tags; +} +``` + +But this is very bad, because Doctrine collections should not be swapped, mostly because collections are managed by +an ObjectManager, thus it must not be replaced by a new instance. + +Once again, two cases may arise: the tags already exist or they does not. + +##### Existing entity in the association + +When the association's entity already exists, what you need to do is simply giving the identifiers of the entities: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost'); +$blogPost = new BlogPost(); +$data = array( + 'title' => 'The best blog post in the world !', + 'tags' => array( + array('id' => 3), // add tag whose id is 3 + array('id' => 8) // also add tag whose id is 8 + ) +); + +$blogPost = $hydrator->hydrate($data, $blogPost); + +echo $blogPost->getTitle(); // prints "The best blog post in the world !" +echo count($blogPost->getTags()); // prints 2 +``` + +**NOTE** : once again, this: + +```php +$data = array( + 'title' => 'The best blog post in the world !', + 'tags' => array( + array('id' => 3), // add tag whose id is 3 + array('id' => 8) // also add tag whose id is 8 + ) +); +``` + +can be written: + +```php +$data = array( + 'title' => 'The best blog post in the world !', + 'tags' => array(3, 8) +); +``` + +##### Non-existing entity in the association + +If the association's entity does not exist, you just need to give the given object: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost'); +$blogPost = new BlogPost(); + +$tags = array(); + +$tag1 = new Tag(); +$tag1->setName('PHP'); +$tags[] = $tag1; + +$tag2 = new Tag(); +$tag2->setName('STL'); +$tags[] = $tag2; + +$data = array( + 'title' => 'The best blog post in the world !', + 'tags' => $tags // Note that you can mix integers and entities without any problem +); + +$blogPost = $hydrator->hydrate($data, $blogPost); + +echo $blogPost->getTitle(); // prints "The best blog post in the world !" +echo count($blogPost->getTags()); // prints 2 +``` + +For this to work, you must also slightly change your mapping, so that Doctrine can persist new entities on +associations (note the cascade options on the OneToMany association): + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class BlogPost +{ + /** .. */ + + /** + * @ORM\OneToMany(targetEntity="Application\Entity\Tag", mappedBy="blogPost", cascade={"persist"}) + */ + protected $tags; + + /** … */ +} +``` + +### Collections strategy + +By default, every collections association has a special strategy attached to it that is called during the hydrating +and extracting phase. All those strategies extend from the class +`DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy`. + +DoctrineModule provides two strategies out of the box: + +1. `DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByValue`: this is the default strategy, it removes old elements that are not in the new collection. +2. `DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByReference`: this is the default strategy (if set to byReference), it removes old elements that are not in the new collection. +3. `DoctrineModule\Stdlib\Hydrator\Strategy\DisallowRemoveByValue`: this strategy does not remove old elements even if they are not in the new collection. +4. `DoctrineModule\Stdlib\Hydrator\Strategy\DisallowRemoveByReference`: this strategy does not remove old elements even if they are not in the new collection. + +As a consequence, when using `AllowRemove*`, you need to define both adder (eg. addTags) and remover (eg. removeTags). +On the other hand, when using the `DisallowRemove*` strategy, you must always define at least the adder, but the remover +is optional (because elements are never removed). + +The following table illustrate the difference between the two strategies + +| Strategy | Initial collection | Submitted collection | Result | +| -------- | ------------------ | -------------------- | ------ | +| AllowRemove* | A, B | B, C | B, C +| DisallowRemove* | A, B | B, C | A, B, C + +The difference between ByValue and ByReference is that when using strategies that end by ByReference, it won't use +the public API of your entity (adder and remover) - you don't even need to define them -. It will directly add and +remove elements directly from the collection. + + +#### Changing the strategy + +Changing the strategy for collections is plain easy. + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use DoctrineModule\Stdlib\Hydrator\Strategy; + +$hydrator = new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost'); +$hydrator->addStrategy('tags', new Strategy\DisallowRemoveByValue()); +``` + +Note that you can also add strategies to simple fields. + + +### By value and by reference + +By default, Doctrine Hydrator works by value. This means that the hydrator will access and modify your properties +through the public API of your entities (that is to say, with getters and setters). However, you can override this +behaviour to work by reference (that is to say that the hydrator will access the properties through Reflection API, +and hence bypass any logic you may include in your setters/getters). + +To change the behaviour, just give the third parameter of the constructor to false: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($objectManager, 'Your\Entity', false); +``` + +To illustrate the difference between, the two, let's do an extraction with the given entity: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class SimpleEntity +{ + /** + * @ORM\Column(type="string") + */ + protected $foo; + + public function getFoo() + { + die(); + } + + /** ... */ +} +``` + +Let's now use the hydrator using the default method, by value: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($objectManager, 'Application\Entity\SimpleEntity'); +$object = new SimpleEntity(); +$object->setFoo('bar'); + +$data = $hydrator->extract($object); + +echo $data['foo']; // never executed, because the script was killed when getter was accessed +``` + +As we can see here, the hydrator used the public API (here getFoo) to retrieve the value. + +However, if we use it by reference: + +```php +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; + +$hydrator = new DoctrineHydrator($objectManager, 'Application\Entity\SimpleEntity', false); +$object = new SimpleEntity(); +$object->setFoo('bar'); + +$data = $hydrator->extract($object); + +echo $data['foo']; // prints 'bar' +``` + +It now only prints "bar", which shows clearly that the getter has not been called. + + +### A complete example using Zend\Form + +Now that we understand how the hydrator works, let's see how it integrates into the Zend Framework 2's Form component. +We are going to use a simple example with, once again, a BlogPost and a Tag entities. We will see how we can create the +blog post, and being able to edit it. + +#### The entities + +First, let's define the (simplified) entities, beginning with the BlogPost entity: + +```php + +namespace Application\Entity; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class BlogPost +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\OneToMany(targetEntity="Application\Entity\Tag", mappedBy="blogPost", cascade={"persist"}) + */ + protected $tags; + + + /** + * Never forget to initialize all your collections ! + */ + public function __construct() + { + $this->tags = new ArrayCollection(); + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * @param Collection $tags + */ + public function addTags(Collection $tags) + { + foreach ($tags as $tag) { + $tag->setBlogPost($this); + $this->tags->add($tag); + } + } + + /** + * @param Collection $tags + */ + public function removeTags(Collection $tags) + { + foreach ($tags as $tag) { + $tag->setBlogPost(null); + $this->tags->removeElement($tag); + } + } + + /** + * @return Collection + */ + public function getTags() + { + return $this->tags; + } +} +``` + +And then the Tag entity: + +```php + +namespace Application\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class Tag +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\ManyToOne(targetEntity="Application\Entity\BlogPost", inversedBy="tags") + */ + protected $blogPost; + + /** + * @ORM\Content(type="string") + */ + protected $name; + + + /** + * Get the id + + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * Allow null to remove association + * + * @param BlogPost $blogPost + */ + public function setBlogPost(BlogPost $blogPost = null) + { + $this->blogPost = $blogPost; + } + + /** + * @return BlogPost + */ + public function getBlogPost() + { + return $this->blogPost; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } +} +``` + +#### The fieldsets + +We now need to create two fieldsets that will map those entities. With Zend Framework 2, it's a good practice to create +one fieldset per entity in order to reuse them accross many forms. + +Here is the fieldset for the Tag. Notice that in this example, I added a hidden input whose name is "id". This is +needed for editing. Most of the time, when you create the Blog Post for the first time, the tags does not exist. +Therefore, the id will be empty. However, when you edit the blog post, all the tags already exists in database (they +have been persisted and have an id), and hence the hidden "id" input will have a value. This allow you to modify a tag +name by modifying an existing Tag entity without creating a new tag (and removing the old one). + +```php + +namespace Application\Form; + +use Application\Entity\Tag; +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Fieldset; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\ServiceManager\ServiceManager; + +class TagFieldset extends Fieldset implements InputFilterProviderInterface +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('tag'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\Tag')) + ->setObject(new Tag()); + + $this->add(array( + 'type' => 'Zend\Form\Element\Hidden', + 'name' => 'id' + )); + + $this->add(array( + 'type' => 'Zend\Form\Element\Text', + 'name' => 'name', + 'options' => array( + 'label' => 'Tag' + ) + )); + } + + public function getInputFilterSpecification() + { + return array( + 'id' => array( + 'required' => false + ), + + 'name' => array( + 'required' => true + ) + ); + } +} +``` + +And the BlogPost fieldset: + +```php + +namespace Application\Form; + +use Application\Entity\BlogPost; +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Fieldset; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\ServiceManager\ServiceManager; + +class BlogPostFieldset extends Fieldset implements InputFilterProviderInterface +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('blog-post'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost')) + ->setObject(new BlogPost()); + + $this->add(array( + 'type' => 'Zend\Form\Element\Text', + 'name' => 'title' + )); + + $this->add(array( + 'type' => 'Zend\Form\Element\Collection', + 'name' => 'tags', + 'options' => array( + 'count' => 2, + 'target_element' => array( + 'type' => 'Application\Form\TagFieldset' + ) + ) + )); + } + + public function getInputFilterSpecification() + { + return array( + 'title' => array( + 'required' => true + ), + ); + } +} +``` + +Plain and easy. The blog post is just a simple fieldset with an element type of type ``Zend\Form\Element\Collection`` +that represents the ManyToOne association. + +#### The form + +Now that we have created our fieldset, we will create two forms: one form for creation and one form for updating. +The form task is to make the glue between the fieldsets. In this simple example, both forms are exactly the same, +but in a real application, you may want to change this behaviour by changing the validation group (for instance, you +may want to disallow the user to modify the title of the blog post when updating). + +Here is the create form: + +```php +namespace Application\Form; + +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Form; +use Zend\ServiceManager\ServiceManager; + +class CreateBlogPostForm extends Form +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('create-blog-post-form'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + // The form will hydrate an object of type "BlogPost" + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost')); + + // Add the user fieldset, and set it as the base fieldset + $blogPostFieldset = new BlogPostFieldset($serviceManager); + $blogPostFieldset->setUseAsBaseFieldset(true); + $this->add($blogPostFieldset); + + // … add CSRF and submit elements … + + // Optionally set your validation group here + } +} +``` + +And the update form: + +```php +namespace Application\Form; + +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Form; +use Zend\ServiceManager\ServiceManager; + +class UpdateBlogPostForm extends Form +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('update-blog-post-form'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + // The form will hydrate an object of type "BlogPost" + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\BlogPost')); + + // Add the user fieldset, and set it as the base fieldset + $blogPostFieldset = new BlogPostFieldset($serviceManager); + $blogPostFieldset->setUseAsBaseFieldset(true); + $this->add($blogPostFieldset); + + // … add CSRF and submit elements … + + // Optionally set your validation group here + } +} +``` + +#### The controllers + +We now have everything. Let's create the controllers. + +##### Creation + +If the createAction, we will create a new BlogPost and all the associated tags. As a consequence, the hidden ids +for the tags will by empty (because they have not been persisted yet). + +Here is the action for create a new blog post: + +```php + +public function createAction() +{ + // Create the form + $form = new CreateBlogPostForm($this->serviceLocator); + + // Create a new, empty entity and bind it to the form + $blogPost = new BlogPost(); + $form->bind($blogPost); + + if ($this->request->isPost()) { + $form->setData($this->request->getPost()); + + if ($form->isValid()) { + $this->entityManager->persist($blogPost); + $this->entityManager->flush(); + } + } + + return array('form' => $form); +} +``` + +The update form is similar, instead that we get the blog post from database instead of creating an empty one: + +```php + +public function editAction() +{ + // Create the form + $form = new UpdateBlogPostForm($this->serviceLocator); + + // Create a new, empty entity and bind it to the form + $blogPost = $this->userService->get($this->params('blogPost_id')); + $form->bind($blogPost); + + if ($this->request->isPost()) { + $form->setData($this->request->getPost()); + + if ($form->isValid()) { + // Save the changes + $this->entityManager->flush(); + } + } + + return array('form' => $form); +} +``` + + + +### Performance considerations + +Although using the hydrator is like magical as it abstracts most of the tedious task, you have to be aware that it can +leads to performance issues in some situations. Please carefully read the following paragraphs in order to know how +to solve (and avoid !) them. + +#### Make hydrator get a reference instead of a database call + +By default, the DoctrineModule hydrator performs a "find" operation for every relationships, and hence retrieving the +whole entity from database. This is not always the wanted behaviour (but it can be !), and it can leads to performance +problems. Most of the time, what you want is just retrieving a reference to this object, instead of fetching it from +database. + +If you are using Doctrine 2 ORM, you have to use the hydrator from DoctrineORMModule (instead of the one from +DoctrineModule). The usage is exactly the same, except that instead of a `find` call, it makes a `getReference` +call. This is up to you to choose the right hydrator for your specific need. + +#### Unwanting side-effect + +You have to be very careful when you are using DoctrineModule hydrator with complex entities that contain a lot of +associations, as a lot of unnecessary calls to database can be made if you are not perfectly aware of what happen +under the hood. To explain this problem, let's have an example. + +Imagine the following entity : + + +```php +namespace Application\Entity; + +/** + * @ORM\Entity + * @ORM\Table(name="Students") + */ +class User +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + /** + * @ORM\Column(type="string", length=48) + */ + protected $name; + + /** + * @ORM\OneToOne(targetEntity="City") + */ + protected $city; + + // … getter and setters are defined … +} +``` + +This simple entity contains an id, a string property, and a OneToOne relationship. If you are using Zend Framework 2 +forms the correct way, you will likely have a fieldset for every entity, so that you have a perfect mapping between +entities and fieldsets. Here are fieldsets for User and and City entities. + +> If you are not comfortable with Fieldsets and how they should work, please refer to [this part of Zend Framework 2 +documentation](http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html). + +First the User fieldset : + +```php +namespace Application\Form; + +use Application\Entity\User; +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Fieldset; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\ServiceManager\ServiceManager; + +class UserFieldset extends Fieldset implements InputFilterProviderInterface +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('user'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\User')) + ->setObject(new User()); + + $this->add(array( + 'type' => 'Zend\Form\Element\Text', + 'name' => 'name', + 'options' => array( + 'label' => 'Your name' + ), + 'attributes' => array( + 'required' => 'required' + ) + )); + + $cityFieldset = new CityFieldset($serviceManager); + $cityFieldset->setLabel('Your city'); + $cityFieldset->setName('city'); + $this->add($cityFieldset); + } + + public function getInputFilterSpecification() + { + return array( + 'name' => array( + 'required' => true + ) + ); + } +} + +``` + +And then the City fieldset : + +```php +namespace Application\Form; + +use Application\Entity\City; +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Fieldset; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\ServiceManager\ServiceManager; + +class CityFieldset extends Fieldset implements InputFilterProviderInterface +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('city'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\Entity\City')) + ->setObject(new City()); + + $this->add(array( + 'type' => 'Zend\Form\Element\Text', + 'name' => 'name', + 'options' => array( + 'label' => 'Name of your city' + ), + 'attributes' => array( + 'required' => 'required' + ) + )); + + $this->add(array( + 'type' => 'Zend\Form\Element\Text', + 'name' => 'postCode', + 'options' => array( + 'label' => 'Postcode of your city' + ), + 'attributes' => array( + 'required' => 'required' + ) + )); + } + + public function getInputFilterSpecification() + { + return array( + 'name' => array( + 'required' => true + ), + + 'postCode' => array( + 'required' => true + ) + ); + } +} + +``` + +Now, let's say that we have one form where a logged user can only change his name. This specific form does not allow +the user to change this city, and the fields of the city are not even rendered in the form. Naively, this form would +be like this : + +```php +namespace Application\Form; + +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Form; +use Zend\ServiceManager\ServiceManager; + +class EditNameForm extends Form +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('edit-name-form'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\User\Entity')); + + // Add the user fieldset, and set it as the base fieldset + $userFieldset = new UserFieldset($serviceManager); + $userFieldset->setName('user'); + $userFieldset->setUseAsBaseFieldset(true); + $this->add($userFieldset); + + // … add CSRF and submit elements … + + // Set the validation group so that we don't care about city + $this->setValidationGroup(array( + 'csrf', // assume we added a CSRF element + 'user' => array( + 'name' + ) + )); + } +} +``` + +> Once again, if you are not familiar with the concepts here, please read the [official documentation about that](http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html). + +Here, we create a simple form called "EditSimpleForm". Because we set the validation group, all the inputs related +to city (postCode and name of the city) won't be validated, which is exactly what we want. The action will look +something like this : + +```php +public function editNameAction() +{ + // Create the form + $form = new EditNameForm(); + + // Get the logged user (for more informations about userIdentity(), please read the Authentication doc) + $loggedUser = $this->userIdentity(); + + // We bind the logged user to the form, so that the name is pre-filled with previous data + $form->bind($loggedUser); + + $request = $this->request; + if ($request->isPost()) { + // Set data from post + $form->setData($request->getPost()); + + if ($form->isValid()) { + // You can now safely save $loggedUser + } + } +} +``` + +This looks good, isn't it ? However, if we check the queries that are made (for instance using the awesome +[ZendDeveloperTools module](https://github.com/zendframework/ZendDeveloperTools)), we will see that a request is +made to fetch data for the City relationship of the user, and we hence have a completely useless database call, +as this information is not rendered by the form. + +You could ask, why ? Yes, we set the validation group, BUT the problem happens during the extracting phase. Here is +how it works : when an object is bound to the form, this latter iterates through all its fields, and tries to extract +the data from the object that is bound. In our example, here is how it work : + +1. It first arrives to the UserFieldset. The input are "name" (which is string field), and a "city" which is another fieldset (in our User entity, this is a OneToOne relationship to another entity). The hydrator will extract both the name and the city (which will be a Doctrine 2 Proxy object). +2. Because the UserFieldset contains a reference to another Fieldset (in our case, a CityFieldset), it will, in turn, tries to extract the values of the City to populate the values of the CityFieldset. And here is the problem : City is a Proxy, and hence because the hydrator tries to extract its values (the name and postcode field), Doctrine will automatically fetch the object from the database in order to please the hydrator. + +This is absolutely normal, this is how ZF 2 forms work and what make them nearly magic, but in this specific case, it +can leads to disastrous consequences. When you have very complex entities with a lot of OneToMany collections, imagine +how many unnecessary calls can be made (actually, after discovering this problem, I've realized that my applications was +doing 10 unnecessary database calls). + +In fact, the fix is ultra simple : if you don't need specific fieldsets in a form, remove them. Here is the fix +EditUserForm : + +```php +namespace Application\Form; + +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator; +use Zend\Form\Form; +use Zend\ServiceManager\ServiceManager; + +class EditNameForm extends Form +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('edit-name-form'); + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + $this->setHydrator(new DoctrineHydrator($entityManager, 'Application\User\Entity')); + + // Add the user fieldset, and set it as the base fieldset + $userFieldset = new UserFieldset($serviceManager); + $userFieldset->setName('user'); + $userFieldset->setUseAsBaseFieldset(true); + + // We don't want City relationship, so remove it !! + $userFieldset->remove('city'); + + $this->add($userFieldset); + + // … add CSRF and submit elements … + + // We don't even need the validation group as the City fieldset does not + // exist anymore + } +} +``` + +And boom ! As the UserFieldset does not contain the CityFieldset relation anymore, it won't be extracted ! + +As a rule of thumb, try to remove any unnecessary fieldset relationship, and always look at which database calls are made. diff --git a/vendor/doctrine/doctrine-module/docs/index.md b/vendor/doctrine/doctrine-module/docs/index.md new file mode 100644 index 00000000..bf44980e --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/index.md @@ -0,0 +1,19 @@ +# DoctrineModule + +DoctrineModule provides a bridge between Zend Framework 2 and Doctrine 2. +It gives you access to features that can be used across Doctrine 2 ORM as well as Doctrine 2 ODM. +It provides an abstraction layer on top of [`Doctrine\Common`](https://github.com/doctrine/common) +which allows the end user to build functionality being completely unaware if he's currently working +with Doctrine ORM or Doctrine MongoDB ODM. + +To use Doctrine ORM or ODM, you will need [DoctrineORMModule](https://github.com/doctrine/DoctrineORMModule) +or [DoctrineMongoODMModule](https://github.com/doctrine/DoctrineMongoODMModule) respectively. + +You can find more details about the features offered by DoctrineModule: + +* [Authentication documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/authentication.md): this explains how you can use the DoctrineModule authentication adapter and authentication storage adapter to provide a simple way to authenticate users using Doctrine. +* [Caching documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/caching.md): DoctrineModule provides simple classes to allow easier caching using Doctrine. +* [CLI documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/cli.md): learn how to use the Doctrine 2 command line tool, and how to add your own command. +* [Hydrator documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/hydrator.md): if you are using Zend Framework 2 Forms (and I hope you are !), DoctrineModule hydrator provides a powerful hydrator that allow you to easily deal with OneToOne, OneToMany and ManyToOne relationships when using forms. +* [Paginator documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/paginator.md): discover how to use the DoctrineModule Paginator adapter. +* [Validator documentation](https://github.com/doctrine/DoctrineModule/blob/master/docs/validator.md): this chapter explains how to use ObjectExists and NoObjectExists validator, that allow you to easily validate if a given entity exists or not. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/paginator.md b/vendor/doctrine/doctrine-module/docs/paginator.md new file mode 100644 index 00000000..ad7ba4a8 --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/paginator.md @@ -0,0 +1,30 @@ +## Paginator + +DoctrineModule provides a simple Paginator adapter that can be used with DoctrineCollection. + +> Note : if you are using Doctrine 2 ORM, what you are looking for is more likely a Paginator adapter that can be used with Doctrine 2 Paginators. Hopefully, DoctrineORMModule provides such a paginator adapter. You can find the documentation here : + +### Simple example + +Here is how you can use the DoctrineModule paginator adapter : + +```php +use Doctrine\Common\Collections\ArrayCollection; +use DoctrineModule\Paginator\Adapter\Collection as CollectionAdapter; +use Zend\Paginator\Paginator; + +// Create a Doctrine 2 Collection +$doctrineCollection = new ArrayCollection(range(1, 101)); + +// Create the adapter +$adapter = new CollectionAdapter($doctrineCollection); + +// Create the paginator itself +$paginator = new Paginator($adapter); +$paginator->setCurrentPageNumber(1) + ->setItemCountPerPage(5); + +// Pass it to the view, and use it like a "standard" Zend paginator +``` + +For more information about Zend Paginator, please read the [Zend Paginator documentation](http://framework.zend.com/manual/2.0/en/modules/zend.paginator.introduction.html). \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/docs/validator.md b/vendor/doctrine/doctrine-module/docs/validator.md new file mode 100644 index 00000000..9a1dba68 --- /dev/null +++ b/vendor/doctrine/doctrine-module/docs/validator.md @@ -0,0 +1,162 @@ +## Validator + +DoctrineModule provides three validators that work out the box : `DoctrineModule\Validator\ObjectExists` and `DoctrineModule\Validator\NoObjectExists` that allow to check if an entity or does not exists in database, respectively; `DoctrineModule\Validator\UniqueObject` that allows to check if a value is only used in one object. They work like any other standard Zend validators. + +All three validators accept the following options : + +* `object_repository` : an instance of an object repository. +* `fields` : an array that contains all the fields that are used to check if the entity exists (or does not). + +The `DoctrineModule\Validator\UniqueObject` also needs the following option: + +* `object_manager` : an instance of an object manager. + +> Tip : to get an object repository (in Doctrine ORM this is called an entity repository) from an object manager (in Doctrine ORM this is called an entity manager), you need to call the `getRepository` function of any valid object manager instance, passing it the FQCN of the class. For instance, in the context of Doctrine 2 ORM, here is how you get the `object_repository` of the 'Application\Entity\User' entity : + +```php +$repository = $entityManager->getRepository('Application\Entity\User'); +``` + +### Simple usage + +You can directly instantiate a validator the following way: + +```php +$validator = new \DoctrineModule\Validator\ObjectExists(array( + 'object_repository' => $objectManager->getRepository('Application\Entity\User'), + 'fields' => array('email') +)); + +var_dump($validator->isValid('test@example.com')); // dumps 'true' if an entity matches +var_dump($validator->isValid(array('email' => 'test@example.com'))); // dumps 'true' if an entity matches +``` + +### Use together with Zend Framework 2 forms + +Of course, validators are especially useful together with forms. And this is deadly simple. Here is how you would add a `NoObjectExists` validator to a form element (for more details about Form, please refer to the official Zend Framework 2 documentation) : + +```php +namespace Application\Form; + +use DoctrineModule\Validator\NoObjectExists as NoObjectExistsValidator; +use Zend\Form\Form; +use Zend\ServiceManager\ServiceManager; + +class User extends Form +{ + public function __construct(ServiceManager $serviceManager) + { + parent::__construct('my-form'); + + // Add an element + $this->add(array( + 'type' => 'Zend\Form\Element\Email', + 'name' => 'email', + 'options' => array( + 'label' => 'Email' + ), + 'attributes' => array( + 'required' => 'required' + ) + )); + + // add other elements (submit, CSRF…) + + // Fetch any valid object manager from the Service manager (here, an entity manager) + $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); + + // Now get the input filter of the form, and add the validator to the email input + $emailInput = $this->getInputFilter()->get('email'); + + $noObjectExistsValidator = new NoObjectExistsValidator(array( + 'object_repository' => $entityManager->getRepository('Application\Entity\User'), + 'fields' => 'email' + )); + + $emailInput->getValidatorChain() + ->addValidator($noObjectExistsValidator); + } +} +``` + +Of course, if you are using fieldsets, you can directly add the validator using the array notation, for instance in the `getInputFilterSpecification` function, as shown here : + +```php +namespace Application\Form; + +use Zend\Form\Fieldset; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\ServiceManager\ServiceManager; + +class UserFieldset extends Fieldset implements InputFilterProviderInterface +{ + protected $serviceManager; + + public function __construct(ServiceManager $serviceManager) + { + $this->serviceManager = $serviceManager; + + parent::__construct('my-fieldset'); + + // Add an element + $this->add(array( + 'type' => 'Zend\Form\Element\Email', + 'name' => 'email', + 'options' => array( + 'label' => 'Email' + ), + 'attributes' => array( + 'required' => 'required' + ) + )); + } + + public function getInputFilterSpecification() + { + $entityManager = $this->serviceManager->get('Doctrine\ORM\EntityManager'); + + return array( + 'email' => array( + 'validators' => array( + array( + 'name' => 'DoctrineModule\Validator\NoObjectExists', + 'options' => array( + 'object_manager' => $entityManager->getRepository('Application\Entity\User'), + 'fields' => 'email' + ) + ) + ) + ) + ); + } +} +``` + +You can change the default message of the validators the following way : + +```php + +// For NoObjectExists validator (using array notation) : +'validators' => array( + array( + 'name' => 'DoctrineModule\Validator\NoObjectExists', + 'options' => array( + 'object_manager' => $this->getEntityManager()->getRepository('Application\Entity\User'), + 'fields' => 'email' + ), + **'messages' => array( + 'objectFound' => 'Sorry guy, a user with this email already exists !' + )** + ) +) + +// For ObjectExists validator (using object notation) : +$objectExistsValidator = new \DoctrineModule\Validator\ObjectExists(array( + 'object_repository' => $entityManager->getRepository('Application\Entity\User'), + 'fields' => 'email' +)); + +**$objectExistsValidator->setMessage('noObjectFound', 'Sorry, we expect that this email exists !');** +``` + +> Note : as you can see, in order to create a validator in your form objects, you need an object repository, and hence you need to have access to the service manager in order to fetch it (this is also the case for other features from DoctrineModule like custom Form elements). However, when dealing with complex forms, you can have a very deep hierarchy of fieldsets, and "transferring" the service manager from one fieldset to another can be a tedious task, and bring useless complexity to your code, especially if only the deepest fieldset effectively needs the service manager. When dealing with such cases, I have found that the simplest case is to use a Registry. I perfectly know that registry was removed from Zend Framework 2, and it is considered bad practice as it makes testing harder. However, for this very specific use case, I found that this is a nice way to solve the problem. But remember, don't tend to take the easy way out, and don't use this Registry trick every where in your program. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Adapter/ObjectRepository.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Adapter/ObjectRepository.php new file mode 100644 index 00000000..1e7289bb --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Adapter/ObjectRepository.php @@ -0,0 +1,240 @@ +. + */ + +namespace DoctrineModule\Authentication\Adapter; + +use DoctrineModule\Options\Authentication as AuthenticationOptions; +use Zend\Authentication\Adapter\AdapterInterface; +use Zend\Authentication\Adapter\Exception; +use Zend\Authentication\Result as AuthenticationResult; + +/** + * Authentication adapter that uses a Doctrine object for verification. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.5.0 + * @author Tim Roediger + * @author Michaël Gallego + */ +class ObjectRepository implements AdapterInterface +{ + /** + * User supplied identity. + * + * @var string + */ + protected $identityValue; + + /** + * User supplied credential. + * + * @var string + */ + protected $credentialValue; + + /** + * @var AuthenticationOptions + */ + protected $options; + + /** + * Contains the authentication results. + * + * @var array + */ + protected $authenticationResultInfo = null; + + /** + * Constructor + * + * @param array|AuthenticationOptions $options + */ + public function __construct($options = array()) + { + $this->setOptions($options); + } + + /** + * @param array|AuthenticationOptions $options + * @return ObjectRepository + */ + public function setOptions($options) + { + if (!$options instanceof AuthenticationOptions) { + $options = new AuthenticationOptions($options); + } + + $this->options = $options; + return $this; + } + + /** + * Set the value to be used as the identity + * + * @param mixed $identityValue + * @return ObjectRepository + */ + public function setIdentityValue($identityValue) + { + $this->identityValue = $identityValue; + return $this; + } + + /** + * @return string + */ + public function getIdentityValue() + { + return $this->identityValue; + } + + /** + * Set the credential value to be used. + * + * @param mixed $credentialValue + * @return ObjectRepository + */ + public function setCredentialValue($credentialValue) + { + $this->credentialValue = $credentialValue; + return $this; + } + + /** + * @return string + */ + public function getCredentialValue() + { + return $this->credentialValue; + } + + /** + * {@inheritDoc} + */ + public function authenticate() + { + $this->setup(); + $options = $this->options; + $identity = $options->getObjectRepository()->findOneBy(array($options->getIdentityProperty() => $this->identityValue)); + + if (!$identity) { + $this->authenticationResultInfo['code'] = AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND; + $this->authenticationResultInfo['messages'][] = 'A record with the supplied identity could not be found.'; + + return $this->createAuthenticationResult(); + } + + $authResult = $this->validateIdentity($identity); + + return $authResult; + } + + /** + * This method attempts to validate that the record in the resultset is indeed a + * record that matched the identity provided to this adapter. + * + * @param object $identity + * @throws Exception\UnexpectedValueException + * @return AuthenticationResult + */ + protected function validateIdentity($identity) + { + $credentialProperty = $this->options->getCredentialProperty(); + $getter = 'get' . ucfirst($credentialProperty); + $documentCredential = null; + + if (method_exists($identity, $getter)) { + $documentCredential = $identity->$getter(); + } elseif (property_exists($identity, $credentialProperty)) { + $documentCredential = $identity->{$credentialProperty}; + } else { + throw new Exception\UnexpectedValueException(sprintf( + 'Property (%s) in (%s) is not accessible. You should implement %s::%s()', + $credentialProperty, + get_class($identity), + get_class($identity), + $getter + )); + } + + $credentialValue = $this->credentialValue; + $callable = $this->options->getCredentialCallable(); + + if ($callable) { + $credentialValue = call_user_func($callable, $identity, $credentialValue); + } + + if ($credentialValue !== true && $credentialValue !== $documentCredential) { + $this->authenticationResultInfo['code'] = AuthenticationResult::FAILURE_CREDENTIAL_INVALID; + $this->authenticationResultInfo['messages'][] = 'Supplied credential is invalid.'; + + return $this->createAuthenticationResult(); + } + + $this->authenticationResultInfo['code'] = AuthenticationResult::SUCCESS; + $this->authenticationResultInfo['identity'] = $identity; + $this->authenticationResultInfo['messages'][] = 'Authentication successful.'; + + return $this->createAuthenticationResult(); + } + + /** + * This method abstracts the steps involved with making sure that this adapter was + * indeed setup properly with all required pieces of information. + * + * @throws Exception\RuntimeException - in the event that setup was not done properly + */ + protected function setup() + { + if (null === $this->identityValue) { + throw new Exception\RuntimeException( + 'A value for the identity was not provided prior to authentication with ObjectRepository authentication ' + . 'adapter' + ); + } + + if (null === $this->credentialValue) { + throw new Exception\RuntimeException( + 'A credential value was not provided prior to authentication with ObjectRepository authentication adapter' + ); + } + + $this->authenticationResultInfo = array( + 'code' => AuthenticationResult::FAILURE, + 'identity' => $this->identityValue, + 'messages' => array() + ); + } + + /** + * Creates a Zend\Authentication\Result object from the information that has been collected + * during the authenticate() attempt. + * + * @return \Zend\Authentication\Result + */ + protected function createAuthenticationResult() + { + return new AuthenticationResult( + $this->authenticationResultInfo['code'], + $this->authenticationResultInfo['identity'], + $this->authenticationResultInfo['messages'] + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Storage/ObjectRepository.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Storage/ObjectRepository.php new file mode 100644 index 00000000..eb280ca3 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Authentication/Storage/ObjectRepository.php @@ -0,0 +1,118 @@ +. + */ + +namespace DoctrineModule\Authentication\Storage; + +use DoctrineModule\Options\Authentication as AuthenticationOptions; +use Zend\Authentication\Storage\StorageInterface; + +/** + * This class implements StorageInterface and allow to save the result of an authentication against an object repository + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.5.0 + * @author Michaël Gallego + */ +class ObjectRepository implements StorageInterface +{ + + /** + * + * @var \DoctrineModule\Options\Authentication + */ + protected $options; + + /** + * @param array | \DoctrineModule\Options\Authentication $options + * @return ObjectRepository + */ + public function setOptions($options) + { + if (!$options instanceof AuthenticationOptions) { + $options = new AuthenticationOptions($options); + } + + $this->options = $options; + return $this; + } + + /** + * Constructor + * + * @param array | \DoctrineModule\Options\Authentication $options + */ + public function __construct($options = array()) + { + $this->setOptions($options); + } + + /** + * @return bool + */ + public function isEmpty() + { + return $this->options->getStorage()->isEmpty(); + } + + /** + * This function assumes that the storage only contains identifier values (which is the case if + * the ObjectRepository authentication adapter is used). + * + * @return null|object + */ + public function read() + { + if (($identity = $this->options->getStorage()->read())) { + return $this->options->getObjectRepository()->find($identity); + } + + return null; + } + + /** + * Will return the key of the identity. If only the key is needed, this avoids an + * unnecessary db call + * + * @return mixed + */ + public function readKeyOnly(){ + return $identity = $this->options->getStorage()->read(); + } + + /** + * @param object $identity + * @return void + */ + public function write($identity) + { + $metadataInfo = $this->options->getClassMetadata(); + $identifierValues = $metadataInfo->getIdentifierValues($identity); + + $this->options->getStorage()->write($identifierValues); + } + + /** + * @return void + */ + public function clear() + { + $this->options->getStorage()->clear(); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/DoctrineCacheStorage.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/DoctrineCacheStorage.php new file mode 100644 index 00000000..3a966870 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/DoctrineCacheStorage.php @@ -0,0 +1,90 @@ +. + */ + +namespace DoctrineModule\Cache; + +use Doctrine\Common\Cache\Cache; +use Zend\Cache\Storage\Adapter\AbstractAdapter; + +/** + * Bridge class that allows usage of a Doctrine Cache Storage as a Zend Cache Storage + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class DoctrineCacheStorage extends AbstractAdapter +{ + /** + * @var Cache + */ + protected $cache; + + /** + * {@inheritDoc} + * @param Cache $cache + */ + public function __construct($options, Cache $cache) + { + parent::__construct($options); + + $this->cache = $cache; + } + + /** + * {@inheritDoc} + */ + protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null) + { + $key = $this->getOptions()->getNamespace() . $normalizedKey; + $fetched = $this->cache->fetch($key); + $success = ($fetched === false ? false : true); + + if ($success) { + $casToken = $fetched; + + return $fetched; + } + + return null; + } + + /** + * {@inheritDoc} + */ + protected function internalSetItem(& $normalizedKey, & $value) + { + $key = $this->getOptions()->getNamespace() . $normalizedKey; + + return $this->cache->save($key, $value); + } + + /** + * {@inheritDoc} + */ + protected function internalRemoveItem(& $normalizedKey) + { + $key = $this->getOptions()->getNamespace() . $normalizedKey; + if (!$this->cache->contains($key)) { + return false; + } + + return $this->cache->delete($key); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/ZendStorageCache.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/ZendStorageCache.php new file mode 100644 index 00000000..8aa6a264 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Cache/ZendStorageCache.php @@ -0,0 +1,123 @@ +. + */ + +namespace DoctrineModule\Cache; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\CacheProvider; +use Zend\Cache\Storage\StorageInterface; +use Zend\Cache\Storage\FlushableInterface; +use Zend\Cache\Storage\TotalSpaceCapableInterface; +use Zend\Cache\Storage\AvailableSpaceCapableInterface; + +/** + * Bridge class that allows usage of a Zend Cache Storage as a Doctrine Cache + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ZendStorageCache extends CacheProvider +{ + + /** + * @var StorageInterface + */ + protected $storage; + + /** + * @param StorageInterface $storage + */ + public function __construct(StorageInterface $storage) + { + $this->storage = $storage; + } + + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + $hit = $this->storage->getItem($id); + + return null === $hit ? false : $hit; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + return $this->storage->hasItem($id); + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = false) + { + // @todo check if lifetime can be set + return $this->storage->setItem($id, $data); + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + return $this->storage->removeItem($id); + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + if ($this->storage instanceof FlushableInterface) { + /* @var $storage FlushableInterface */ + $storage = $this->storage; + + return $storage->flush(); + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + /* @var $storage TotalSpaceCapableInterface */ + /* @var $storage AvailableSpaceCapableInterface */ + $storage = $this->storage; + + return array( + Cache::STATS_HITS => $this->storage->getMetadata(Cache::STATS_HITS), + Cache::STATS_MISSES => $this->storage->getMetadata(Cache::STATS_MISSES), + Cache::STATS_UPTIME => $this->storage->getMetadata(Cache::STATS_UPTIME), + Cache::STATS_MEMORY_USAGE => $storage instanceof TotalSpaceCapableInterface + ? $storage->getTotalSpace() + : null, + Cache::STATS_MEMORY_AVAILIABLE => $storage instanceof AvailableSpaceCapableInterface + ? $storage->getAvailableSpace() + : null, + ); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectMultiCheckbox.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectMultiCheckbox.php new file mode 100644 index 00000000..87e3c794 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectMultiCheckbox.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineModule\Form\Element; + +use DoctrineModule\Form\Element\Proxy; +use Zend\Form\Element\MultiCheckbox; +use Zend\Form\Form; + +class ObjectMultiCheckbox extends MultiCheckbox +{ + /** + * @var Proxy + */ + protected $proxy; + + /** + * @return Proxy + */ + public function getProxy() + { + if (null === $this->proxy) { + $this->proxy = new Proxy(); + } + return $this->proxy; + } + + /** + * @param array|\Traversable $options + * @return ObjectSelect + */ + public function setOptions($options) + { + $this->getProxy()->setOptions($options); + return parent::setOptions($options); + } + + /** + * {@inheritDoc} + */ + public function setValue($value) + { + return parent::setValue($this->getProxy()->getValue($value)); + } + + /** + * {@inheritDoc} + */ + public function getValueOptions() + { + if (empty($this->valueOptions)) { + $this->setValueOptions($this->getProxy()->getValueOptions()); + } + return $this->valueOptions; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectRadio.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectRadio.php new file mode 100644 index 00000000..9e004401 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectRadio.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineModule\Form\Element; + +use DoctrineModule\Form\Element\Proxy; +use Zend\Form\Element\Radio as RadioElement; +use Zend\Form\Form; + +class ObjectRadio extends RadioElement +{ + /** + * @var Proxy + */ + protected $proxy; + + /** + * @return Proxy + */ + public function getProxy() + { + if (null === $this->proxy) { + $this->proxy = new Proxy(); + } + return $this->proxy; + } + + /** + * @param array|\Traversable $options + * @return ObjectSelect + */ + public function setOptions($options) + { + $this->getProxy()->setOptions($options); + return parent::setOptions($options); + } + + /** + * {@inheritDoc} + */ + public function setValue($value) + { + return parent::setValue($this->getProxy()->getValue($value)); + } + + /** + * {@inheritDoc} + */ + public function getValueOptions() + { + if (empty($this->valueOptions)) { + $this->setValueOptions($this->getProxy()->getValueOptions()); + } + return $this->valueOptions; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectSelect.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectSelect.php new file mode 100644 index 00000000..ef04adc0 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/ObjectSelect.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineModule\Form\Element; + +use DoctrineModule\Form\Element\Proxy; +use Zend\Form\Element\Select as SelectElement; +use Zend\Form\Form; + +class ObjectSelect extends SelectElement +{ + /** + * @var Proxy + */ + protected $proxy; + + /** + * @return Proxy + */ + public function getProxy() + { + if (null === $this->proxy) { + $this->proxy = new Proxy(); + } + return $this->proxy; + } + + /** + * @param array|\Traversable $options + * @return ObjectSelect + */ + public function setOptions($options) + { + $this->getProxy()->setOptions($options); + return parent::setOptions($options); + } + + /** + * {@inheritDoc} + */ + public function setValue($value) + { + return parent::setValue($this->getProxy()->getValue($value)); + } + + /** + * {@inheritDoc} + */ + public function getValueOptions() + { + if (empty($this->valueOptions)) { + $this->setValueOptions($this->getProxy()->getValueOptions()); + } + return $this->valueOptions; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php new file mode 100644 index 00000000..e9edc4b0 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Form/Element/Proxy.php @@ -0,0 +1,361 @@ +. + */ + +namespace DoctrineModule\Form\Element; + +use RuntimeException; +use ReflectionMethod; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\ObjectManager; +use DoctrineModule\Persistence\ObjectManagerAwareInterface; + +class Proxy implements ObjectManagerAwareInterface +{ + /** + * @var array + */ + protected $objects; + + /** + * @var string + */ + protected $targetClass; + + /** + * @var array + */ + protected $valueOptions = array(); + + /** + * @var array + */ + protected $findMethod = array(); + + /** + * @var + */ + protected $property; + + /** + * @var + */ + protected $isMethod; + + /** + * @var ObjectManager + */ + protected $objectManager; + + public function setOptions($options) + { + if (isset($options['object_manager'])) { + $this->setObjectManager($options['object_manager']); + } + + if (isset($options['target_class'])) { + $this->setTargetClass($options['target_class']); + } + + if (isset($options['property'])) { + $this->setProperty($options['property']); + } + + if (isset($options['find_method'])) { + $this->setFindMethod($options['find_method']); + } + + if (isset($options['is_method'])) { + $this->setIsMethod($options['is_method']); + } + } + + public function getValueOptions() + { + if (empty($this->valueOptions)) { + $this->loadValueOptions(); + } + return $this->valueOptions; + } + + /** + * @return array + */ + public function getObjects() + { + $this->loadObjects(); + return $this->objects; + } + + /** + * Set the object manager + * + * @param ObjectManager $objectManager + * @return Proxy + */ + public function setObjectManager(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + return $this; + } + + /** + * Get the object manager + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Set the FQCN of the target object + * + * @param string $targetClass + * @return Proxy + */ + public function setTargetClass($targetClass) + { + $this->targetClass = $targetClass; + return $this; + } + + /** + * Get the target class + * + * @return string + */ + public function getTargetClass() + { + return $this->targetClass; + } + + /** + * Set the property to use as the label in the options + * + * @param string $property + * @return Proxy + */ + public function setProperty($property) + { + $this->property = $property; + return $this; + } + + /** + * @return mixed + */ + public function getProperty() + { + return $this->property; + } + + /** + * Set if the property is a method to use as the label in the options + * + * @param boolean $method + * @return Proxy + */ + public function setIsMethod($method) + { + $this->isMethod = (bool) $method; + return $this; + } + + /** + * @return mixed + */ + public function getIsMethod() + { + return $this->isMethod; + } + + /** Set the findMethod property to specify the method to use on repository + * + * @param array $findMethod + * @return Proxy + */ + public function setFindMethod($findMethod) + { + $this->findMethod = $findMethod; + return $this; + } + + /** + * Get findMethod definition + * + * @return array + */ + public function getFindMethod() + { + return $this->findMethod; + } + + /** + * @param $value + * @return array|mixed|object + * @throws \RuntimeException + */ + public function getValue($value) + { + if (!($om = $this->getObjectManager())) { + throw new RuntimeException('No object manager was set'); + } + + if (!($targetClass = $this->getTargetClass())) { + throw new RuntimeException('No target class was set'); + } + + $metadata = $om->getClassMetadata($targetClass); + if (is_object($value)) { + if ($value instanceof Collection) { + $data = array(); + foreach($value as $object) { + $values = $metadata->getIdentifierValues($object); + $data[] = array_shift($values); + } + + $value = $data; + } else { + $metadata = $om->getClassMetadata(get_class($value)); + $identifier = $metadata->getIdentifierFieldNames(); + + // TODO: handle composite (multiple) identifiers + if (count($identifier) > 1) { + //$value = $key; + } else { + $value = current($metadata->getIdentifierValues($value)); + } + } + } + + return $value; + } + + /** + * Load objects + * + * @return void + */ + protected function loadObjects() + { + if (!empty($this->objects)) { + return; + } + + $findMethod = (array) $this->getFindMethod(); + if (!$findMethod) { + $this->objects = $this->objectManager->getRepository($this->targetClass)->findAll(); + } else { + if (!isset($this->findMethod['name'])) { + throw new RuntimeException('No method name was set'); + } + $findMethodName = $findMethod['name']; + $findMethodParams = isset($findMethod['params']) ? array_change_key_case($findMethod['params']) : null; + + $repository = $this->objectManager->getRepository($this->targetClass); + if (!method_exists($repository, $findMethodName)) { + throw new RuntimeException(sprintf( + 'Method "%s" could not be found in respository "%s"', + $findMethodName, + get_class($repository) + )); + } + + $r = new ReflectionMethod($repository, $findMethodName); + $args = array(); + foreach ($r->getParameters() as $param) { + if (array_key_exists(strtolower($param->getName()), $findMethodParams)) { + $args[] = $findMethodParams[strtolower($param->getName())]; + } else { + $args[] = $param->getDefaultValue(); + } + } + $this->objects = $r->invokeArgs($repository, $args); + } + } + + /** + * Load value options + * + * @throws \RuntimeException + * @return void + */ + protected function loadValueOptions() + { + if (!($om = $this->objectManager)) { + throw new RuntimeException('No object manager was set'); + } + + if (!($targetClass = $this->targetClass)) { + throw new RuntimeException('No target class was set'); + } + + $metadata = $om->getClassMetadata($targetClass); + $identifier = $metadata->getIdentifierFieldNames(); + $objects = $this->getObjects(); + $options = array(); + + if (empty($objects)) { + $options[''] = ''; + } else { + foreach ($objects as $key => $object) { + if (($property = $this->property)) { + if ($this->isMethod == false && !$metadata->hasField($property)) { + throw new RuntimeException(sprintf( + 'Property "%s" could not be found in object "%s"', + $property, + $targetClass + )); + } + + $getter = 'get' . ucfirst($property); + if (!is_callable(array($object, $getter))) { + throw new RuntimeException(sprintf( + 'Method "%s::%s" is not callable', + $this->targetClass, + $getter + )); + } + + $label = $object->{$getter}(); + } else { + if (!is_callable(array($object, '__toString'))) { + throw new RuntimeException(sprintf( + '%s must have a "__toString()" method defined if you have not set a property or method to use.', + $targetClass + )); + } + + $label = (string) $object; + } + + if (count($identifier) > 1) { + $value = $key; + } else { + $value = current($metadata->getIdentifierValues($object)); + } + + $options[] = array('label' => $label, 'value' => $value); + } + } + + $this->valueOptions = $options; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Module.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Module.php new file mode 100644 index 00000000..778cdb51 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Module.php @@ -0,0 +1,102 @@ +. + */ + +namespace DoctrineModule; + +use Doctrine\Common\Annotations\AnnotationRegistry; +use DoctrineModule\Service\CacheFactory; +use DoctrineModule\Service\ZendStorageCacheFactory; +use Zend\ModuleManager\Feature\AutoloaderProviderInterface; +use Zend\ModuleManager\Feature\InitProviderInterface; +use Zend\ModuleManager\Feature\ConfigProviderInterface; +use Zend\ModuleManager\Feature\ServiceProviderInterface; +use Zend\ModuleManager\ModuleManagerInterface; +use Zend\Loader\AutoloaderFactory; +use Zend\Loader\StandardAutoloader; + +/** + * Base module for integration of Doctrine projects with ZF2 applications + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Kyle Spraggs + * @author Marco Pivetta + */ +class Module implements + AutoloaderProviderInterface, + ConfigProviderInterface, + ServiceProviderInterface, + InitProviderInterface +{ + + /** + * {@inheritDoc} + */ + public function init(ModuleManagerInterface $moduleManager) + { + AnnotationRegistry::registerLoader(function($className) { + return class_exists($className); + }); + } + + /** + * {@inheritDoc} + */ + public function getAutoloaderConfig() + { + return array( + AutoloaderFactory::STANDARD_AUTOLOADER => array( + StandardAutoloader::LOAD_NS => array( + __NAMESPACE__ => __DIR__, + ), + ), + ); + } + + /** + * {@inheritDoc} + */ + public function getConfig() + { + return include __DIR__ . '/../../config/module.config.php'; + } + + /** + * {@inheritDoc} + */ + public function getServiceConfig() + { + return array( + 'factories' => array( + 'doctrine.cli' => 'DoctrineModule\Service\CliFactory', + 'doctrine.cache.apc' => new CacheFactory('apc'), + 'doctrine.cache.array' => new CacheFactory('array'), + 'doctrine.cache.filesystem' => new CacheFactory('filesystem'), + 'doctrine.cache.memcache' => new CacheFactory('memcache'), + 'doctrine.cache.memcached' => new CacheFactory('memcached'), + 'doctrine.cache.redis' => new CacheFactory('redis'), + 'doctrine.cache.wincache' => new CacheFactory('wincache'), + 'doctrine.cache.xcache' => new CacheFactory('xcache'), + 'doctrine.cache.zenddata' => new CacheFactory('zenddata'), + 'doctrine.cache.zendcachestorage' => new ZendStorageCacheFactory('zendcachestorage'), + ), + ); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Authentication.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Authentication.php new file mode 100644 index 00000000..9df050cb --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Authentication.php @@ -0,0 +1,310 @@ +. + */ + +namespace DoctrineModule\Options; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\ObjectRepository; +use Zend\Authentication\Adapter\Exception; +use Zend\Authentication\Storage\Session as SessionStorage; +use Zend\Authentication\Storage\StorageInterface; +use Zend\Stdlib\AbstractOptions; + +/** + * This options class can be consumed by five different classes: + * + * DoctrineModule\Authentication\Adapter\ObjectRepository + * DoctrineModule\Service\Authentication\AdapterFactory + * DoctrineModule\Authentication\Storage\ObjectRepository + * DoctrineModule\Service\Authentication\ServiceFactory + * DoctrineModule\Service\Authentication\AuthenticationServiceFactory + * + * When using with DoctrineModule\Authentication\Adapter\ObjectRepository the following + * options are required: + * + * $identityProperty + * $credentialProperty + * + * In addition either $objectRepository or $objectManager and $identityClass must be set. + * If $objectRepository is set, it takes precedence over $objectManager and $identityClass. + * If $objectManager is used, it must be an instance of ObjectManager. + * + * All remains the same using with DoctrineModule\Service\AuthenticationAdapterFactory, + * however, a string may be passed to $objectManager. This string must be a valid key to + * retrieve an ObjectManager instance from the ServiceManager. + * + * When using with DoctrineModule\Authentication\Service\Object repository the following + * options are required: + * + * Either $objectManager, or $classMetadata and $objectRepository. + * + * All remains the same using with DoctrineModule\Service\AuthenticationStorageFactory, + * however, a string may be passed to $objectManager. This string must be a valid key to + * retrieve an ObjectManager instance from the ServiceManager. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.5.0 + * @author Michaël Gallego + */ +class Authentication extends AbstractOptions +{ + /** + * A valid object implementing ObjectManager interface + * + * @var string | ObjectManager + */ + protected $objectManager; + + /** + * A valid object implementing ObjectRepository interface (or ObjectManager/identityClass) + * + * @var ObjectRepository + */ + protected $objectRepository; + + /** + * Entity's class name + * + * @var string + */ + protected $identityClass; + + /** + * Property to use for the identity + * + * @var string + */ + protected $identityProperty; + + /** + * Property to use for the credential + * + * @var string + */ + protected $credentialProperty; + + /** + * Callable function to check if a credential is valid + * + * @var mixed + */ + protected $credentialCallable; + + /** + * + * If an objectManager is not supplied, this metadata will be used + * by DoctrineModule/Authentication/Storage/ObjectRepository + * + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + protected $classMetadata; + + /** + * When using this options class to create a DoctrineModule/Authentication/Storage/ObjectRepository + * this is the storage instance that the object key will be stored in. + * + * When using this options class to create an AuthenticationService with and + * the option storeOnlyKeys == false, this is the storage instance that the whole + * object will be stored in. + * + * @var \Zend\Authentication\Storage\StorageInterface; + */ + protected $storage; + + /** + * @param string | ObjectManager $objectManager + * @return Authentication + */ + public function setObjectManager($objectManager) + { + $this->objectManager = $objectManager; + return $this; + } + + /** + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * @param ObjectRepository $objectRepository + * @return Authentication + */ + public function setObjectRepository(ObjectRepository $objectRepository) + { + $this->objectRepository = $objectRepository; + return $this; + } + + /** + * @return ObjectRepository + */ + public function getObjectRepository() + { + if ($this->objectRepository) { + return $this->objectRepository; + } + + return $this->objectManager->getRepository($this->identityClass); + } + + /** + * @param string $identityClass + * @return Authentication + */ + public function setIdentityClass($identityClass) + { + $this->identityClass = $identityClass; + return $this; + } + + /** + * @return string + */ + public function getIdentityClass() + { + return $this->identityClass; + } + + /** + * @param string $identityProperty + * @throws Exception\InvalidArgumentException + * @return Authentication + */ + public function setIdentityProperty($identityProperty) + { + if (!is_string($identityProperty) || $identityProperty === '') { + throw new Exception\InvalidArgumentException(sprintf( + 'Provided $identityProperty is invalid, %s given', + gettype($identityProperty) + )); + } + + $this->identityProperty = $identityProperty; + + return $this; + } + + /** + * @return string + */ + public function getIdentityProperty() + { + return $this->identityProperty; + } + + /** + * @param string $credentialProperty + * @throws Exception\InvalidArgumentException + * @return Authentication + */ + public function setCredentialProperty($credentialProperty) + { + if (!is_string($credentialProperty) || $credentialProperty === '') { + throw new Exception\InvalidArgumentException(sprintf( + 'Provided $credentialProperty is invalid, %s given', + gettype($credentialProperty) + )); + } + + $this->credentialProperty = $credentialProperty; + + return $this; + } + + /** + * @return string + */ + public function getCredentialProperty() + { + return $this->credentialProperty; + } + + /** + * @param mixed $credentialCallable + * @throws Exception\InvalidArgumentException + * @return Authentication + */ + public function setCredentialCallable($credentialCallable) + { + if (!is_callable($credentialCallable)) { + throw new Exception\InvalidArgumentException(sprintf( + '"%s" is not a callable', + is_string($credentialCallable) ? $credentialCallable : gettype($credentialCallable) + )); + } + + $this->credentialCallable = $credentialCallable; + + return $this; + } + + /** + * @return mixed + */ + public function getCredentialCallable() + { + return $this->credentialCallable; + } + + /** + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + public function getClassMetadata() { + + if ($this->classMetadata) { + return $this->classMetadata; + } + + return $this->objectManager->getClassMetadata($this->identityClass); + } + + /** + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + */ + public function setClassMetadata(ClassMetadata $classMetadata) { + $this->classMetadata = $classMetadata; + } + + /** + * + * @return \Zend\Authentication\Storage\StorageInterface + */ + public function getStorage() { + if ( ! $this->storage instanceof StorageInterface){ + $this->storage = new SessionStorage(); + } + return $this->storage; + } + + /** + * + * @param \Zend\Authentication\Storage\StorageInterface $storage + */ + public function setStorage(StorageInterface $storage) { + $this->storage = $storage; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Cache.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Cache.php new file mode 100644 index 00000000..178c7b97 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Cache.php @@ -0,0 +1,137 @@ +. + */ + +namespace DoctrineModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * Cache options + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class Cache extends AbstractOptions +{ + /** + * Class used to instantiate the cache. + * + * @var string + */ + protected $class = 'Doctrine\Common\Cache\ArrayCache'; + + /** + * Namespace to prefix all cache ids with. + * + * @var string + */ + protected $namespace = ''; + + /** + * Directory for file-based caching + * + * @var string + */ + protected $directory; + + /** + * Key to use for fetching the memcache, memcached, or redis instance from + * the service locator. Used only with Memcache. Memcached, and Redis. + * + * @var string + */ + protected $instance = null; + + /** + * @param string $class + * @return self + */ + public function setClass($class) + { + $this->class = $class; + + return $this; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * @param string $instance + * @return self + */ + public function setInstance($instance) + { + $this->instance = $instance; + + return $this; + } + + /** + * @return string + */ + public function getInstance() + { + return $this->instance; + } + + /** + * @param string $namespace + * @return self + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + + return $this; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @param string $directory + * @return self + */ + public function setDirectory($directory) + { + $this->directory = $directory; + + return $this; + } + + /** + * @return string + */ + public function getDirectory() + { + return $this->directory; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Driver.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Driver.php new file mode 100644 index 00000000..0c75b165 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/Driver.php @@ -0,0 +1,155 @@ +. + */ + +namespace DoctrineModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * MappingDriver options + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class Driver extends AbstractOptions +{ + /** + * The class name of the Driver. + * + * @var string + */ + protected $class; + + /** + * All drivers (except DriverChain) require paths to work on. You + * may set this value as a string (for a single path) or an array + * for multiple paths. + * + * @var array + */ + protected $paths = array(); + + /** + * Set the cache key for the annotation cache. Cache key + * is assembled as "doctrine.cache.{key}" and pulled from + * service locator. This option is only valid for the + * AnnotationDriver. + * + * @var string + */ + protected $cache = 'array'; + + /** + * Set the file extension to use. This option is only + * valid for FileDrivers (XmlDriver, YamlDriver, PHPDriver, etc). + * + * @var string|null + */ + protected $extension = null; + + /** + * Set the driver keys to use which are assembled as + * "doctrine.driver.{key}" and pulled from the service + * locator. This option is only valid for DriverChain. + * + * @var array + */ + protected $drivers = array(); + + /** + * @param string $cache + */ + public function setCache($cache) + { + $this->cache = $cache; + } + + /** + * @return string + */ + public function getCache() + { + return "doctrine.cache.{$this->cache}"; + } + + /** + * @param string $class + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * @param array $drivers + */ + public function setDrivers($drivers) + { + $this->drivers = $drivers; + } + + /** + * @return array + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * @param null $extension + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * @return string|null + */ + public function getExtension() + { + return $this->extension; + } + + /** + * @param array $paths + */ + public function setPaths($paths) + { + $this->paths = $paths; + } + + /** + * @return array + */ + public function getPaths() + { + return $this->paths; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/EventManager.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/EventManager.php new file mode 100644 index 00000000..bda963e1 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Options/EventManager.php @@ -0,0 +1,60 @@ +. + */ + +namespace DoctrineModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * EventManager options + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class EventManager extends AbstractOptions +{ + /** + * An array of subscribers. The array can contain the FQN of the + * class to instantiate OR a string to be located with the + * service locator. + * + * @var array + */ + protected $subscribers = array(); + + /** + * @param array $subscribers + * @return self + */ + public function setSubscribers($subscribers) + { + $this->subscribers = $subscribers; + + return $this; + } + + /** + * @return array + */ + public function getSubscribers() + { + return $this->subscribers; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Paginator/Adapter/Collection.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Paginator/Adapter/Collection.php new file mode 100644 index 00000000..16e054d9 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Paginator/Adapter/Collection.php @@ -0,0 +1,63 @@ +. + */ + +namespace DoctrineModule\Paginator\Adapter; + +use Doctrine\Common\Collections\Collection as DoctrineCollection; +use Zend\Paginator\Adapter\AdapterInterface; + +/** + * Base module for Doctrine ORM. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Michaël Gallego + * @author Marco Pivetta + */ +class Collection implements AdapterInterface +{ + /** + * @var DoctrineCollection + */ + protected $collection; + + /** + * @param DoctrineCollection $collection + */ + public function __construct(DoctrineCollection $collection) + { + $this->collection = $collection; + } + + /** + * {@inheritDoc} + */ + public function getItems($offset, $itemCountPerPage) + { + return array_values($this->collection->slice($offset, $itemCountPerPage)); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return count($this->collection); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ObjectManagerAwareInterface.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ObjectManagerAwareInterface.php new file mode 100644 index 00000000..ceda119f --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ObjectManagerAwareInterface.php @@ -0,0 +1,39 @@ +. + */ + +namespace DoctrineModule\Persistence; + +use Doctrine\Common\Persistence\ObjectManager; + +interface ObjectManagerAwareInterface +{ + /** + * Set the object manager + * + * @param ObjectManager $objectManager + */ + public function setObjectManager(ObjectManager $objectManager); + + /** + * Get the object manager + * + * @return ObjectManager + */ + public function getObjectManager(); +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ProvidesObjectManager.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ProvidesObjectManager.php new file mode 100644 index 00000000..a095ab83 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Persistence/ProvidesObjectManager.php @@ -0,0 +1,53 @@ +. + */ + +namespace DoctrineModule\Persistence; + +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Trait to provide object manager to a form (only works from PHP 5.4) + */ +trait ProvidesObjectManager +{ + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * Set the object manager + * + * @param ObjectManager $objectManager + */ + public function setObjectManager(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Get the object manager + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/AbstractFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/AbstractFactory.php new file mode 100644 index 00000000..84148e7f --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/AbstractFactory.php @@ -0,0 +1,100 @@ +. + */ + +namespace DoctrineModule\Service; + +use RuntimeException; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Base ServiceManager factory to be extended + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +abstract class AbstractFactory implements FactoryInterface +{ + /** + * @var string + */ + protected $name; + + /** + * @var \Zend\Stdlib\AbstractOptions + */ + protected $options; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Gets options from configuration based on name. + * + * @param ServiceLocatorInterface $sl + * @param string $key + * @param null|string $name + * @return \Zend\Stdlib\AbstractOptions + * @throws \RuntimeException + */ + public function getOptions(ServiceLocatorInterface $sl, $key, $name = null) + { + if ($name === null) { + $name = $this->getName(); + } + + $options = $sl->get('Configuration'); + $options = $options['doctrine']; + $options = isset($options[$key][$name]) ? $options[$key][$name] : null; + + if (null === $options) { + throw new RuntimeException(sprintf( + 'Options with name "%s" could not be found in "doctrine.%s".', + $name, + $key + )); + } + + $optionsClass = $this->getOptionsClass(); + + return new $optionsClass($options); + } + + /** + * Get the class name of the options associated with this factory. + * + * @abstract + * @return string + */ + abstract public function getOptionsClass(); +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AdapterFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AdapterFactory.php new file mode 100644 index 00000000..5bb2f086 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AdapterFactory.php @@ -0,0 +1,53 @@ +. + */ +namespace DoctrineModule\Service\Authentication; + +use DoctrineModule\Authentication\Adapter\ObjectRepository; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Factory to create authentication adapter object. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Tim Roediger + */ +class AdapterFactory extends AbstractFactory +{ + /** + * + * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator + * @return \DoctrineModule\Authentication\Adapter\DoctrineObjectRepository + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + $options = $this->getOptions($serviceLocator, 'authentication'); + if (is_string($options->getObjectManager())) { + $options->setObjectManager($serviceLocator->get($options->getObjectManager())); + } + return new ObjectRepository($options); + } + + public function getOptionsClass() + { + return 'DoctrineModule\Options\Authentication'; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AuthenticationServiceFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AuthenticationServiceFactory.php new file mode 100644 index 00000000..5d08add0 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/AuthenticationServiceFactory.php @@ -0,0 +1,50 @@ +. + */ +namespace DoctrineModule\Service\Authentication; + +use DoctrineModule\Service\AbstractFactory; +use Zend\Authentication\AuthenticationService; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Factory to create authentication service object. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Tim Roediger + */ +class AuthenticationServiceFactory extends AbstractFactory +{ + /** + * + * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator + * @return \Zend\Authentication\AuthenticationService + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + return new AuthenticationService( + $serviceLocator->get('doctrine.authenticationstorage.' . $this->getName()), + $serviceLocator->get('doctrine.authenticationadapter.' . $this->getName()) + ); + } + + public function getOptionsClass() + {} +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/StorageFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/StorageFactory.php new file mode 100644 index 00000000..857d2f5b --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/Authentication/StorageFactory.php @@ -0,0 +1,53 @@ +. + */ +namespace DoctrineModule\Service\Authentication; + +use DoctrineModule\Authentication\Storage\ObjectRepository; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Factory to create authentication storage object. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Tim Roediger + */ +class StorageFactory extends AbstractFactory +{ + /** + * + * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator + * @return \DoctrineModule\Authentication\Adapter\DoctrineObjectRepository + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + $options = $this->getOptions($serviceLocator, 'authentication'); + if (is_string($options->getObjectManager())) { + $options->setObjectManager($serviceLocator->get($options->getObjectManager())); + } + return new ObjectRepository($options); + } + + public function getOptionsClass() + { + return 'DoctrineModule\Options\Authentication'; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CacheFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CacheFactory.php new file mode 100644 index 00000000..30ef9a1b --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CacheFactory.php @@ -0,0 +1,85 @@ +. + */ + +namespace DoctrineModule\Service; + +use RuntimeException; +use Doctrine\Common\Cache\MemcacheCache; +use Doctrine\Common\Cache\MemcachedCache; +use Doctrine\Common\Cache\RedisCache; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Cache ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class CacheFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + * @return \Doctrine\Common\Cache\Cache + * @throws RuntimeException + */ + public function createService(ServiceLocatorInterface $sl) + { + /** @var $options \DoctrineModule\Options\Cache */ + $options = $this->getOptions($sl, 'cache'); + $class = $options->getClass(); + + if (!$class) { + throw new RuntimeException('Cache must have a class name to instantiate'); + } + + if ($class === 'Doctrine\Common\Cache\FilesystemCache') { + $cache = new $class($options->getDirectory()); + } else { + $cache = new $class; + } + + $instance = $options->getInstance(); + if (is_string($instance) && $sl->has($instance)) { + $instance = $sl->get($instance); + } + + if ($cache instanceof MemcacheCache) { + /* @var $cache MemcacheCache */ + $cache->setMemcache($instance); + } elseif ($cache instanceof MemcachedCache) { + /* @var $cache MemcachedCache */ + $cache->setMemcached($instance); + } elseif ($cache instanceof RedisCache) { + /* @var $cache RedisCache */ + $cache->setRedis($instance); + } + + return $cache; + } + + /** + * {@inheritDoc} + */ + public function getOptionsClass() + { + return 'DoctrineModule\Options\Cache'; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CliFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CliFactory.php new file mode 100644 index 00000000..84aa8274 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/CliFactory.php @@ -0,0 +1,89 @@ +. + */ + +namespace DoctrineModule\Service; + +use DoctrineModule\Version; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\ServiceManager; + +/** + * CLI Application ServiceManager factory responsible for instantiating a Symfony CLI application + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class CliFactory implements FactoryInterface +{ + /** + * @var \Zend\EventManager\EventManagerInterface + */ + protected $events; + + /** + * @var \Symfony\Component\Console\Helper\HelperSet + */ + protected $helperSet; + + /** + * @var array + */ + protected $commands = array(); + + /** + * @param ServiceLocatorInterface $sm + * @return \Zend\EventManager\EventManagerInterface + */ + public function getEventManager(ServiceLocatorInterface $sm) + { + if (null === $this->events) { + /* @var $events \Zend\EventManager\EventManagerInterface */ + $events = $sm->get('EventManager'); + $events->addIdentifiers(array( + __CLASS__, + 'doctrine' + )); + + $this->events = $events; + } + + return $this->events; + } + + /** + * {@inheritDoc} + * @return Application + */ + public function createService(ServiceLocatorInterface $sl) + { + $cli = new Application; + $cli->setName('DoctrineModule Command Line Interface'); + $cli->setVersion(Version::VERSION); + $cli->setHelperSet(new HelperSet); + + // Load commands using event + $this->getEventManager($sl)->trigger('loadCli.post', $cli, array('ServiceManager' => $sl)); + + return $cli; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/DriverFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/DriverFactory.php new file mode 100644 index 00000000..4a728b58 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/DriverFactory.php @@ -0,0 +1,135 @@ +. + */ + +namespace DoctrineModule\Service; + +use InvalidArgumentException; +use Doctrine\Common\Annotations; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator; +use DoctrineModule\Options\Driver as DriverOptions; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * MappingDriver ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class DriverFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + * @return MappingDriver + */ + public function createService(ServiceLocatorInterface $sl) + { + /* @var $options DriverOptions */ + $options = $this->getOptions($sl, 'driver'); + + return $this->createDriver($sl, $options); + } + + /** + * {@inheritDoc} + */ + public function getOptionsClass() + { + return 'DoctrineModule\Options\Driver'; + } + + /** + * @param ServiceLocatorInterface $sl + * @param DriverOptions $options + * @throws InvalidArgumentException + * @return MappingDriver + */ + protected function createDriver(ServiceLocatorInterface $sl, DriverOptions $options) + { + $class = $options->getClass(); + + if (!$class) { + throw new InvalidArgumentException('Drivers must specify a class'); + } + + if (!class_exists($class)) { + throw new InvalidArgumentException(sprintf( + 'Driver with type "%s" could not be found', + $class + )); + } + + // Not all drivers (DriverChain) require paths. + $paths = $options->getPaths(); + + // Special options for AnnotationDrivers. + if (($class == 'Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver') || + (is_subclass_of($class, 'Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver'))) + { + $reader = new Annotations\AnnotationReader; + $reader = new Annotations\CachedReader( + new Annotations\IndexedReader($reader), + $sl->get($options->getCache()) + ); + /* @var $driver MappingDriver */ + $driver = new $class($reader, $paths); + } else { + /* @var $driver MappingDriver */ + $driver = new $class($paths); + } + + if ($options->getExtension() && $driver instanceof FileDriver) { + /* @var $driver FileDriver */ + /* @var $locator \Doctrine\Common\Persistence\Mapping\Driver\FileLocator */ + $locator = $driver->getLocator(); + + if (get_class($locator) === 'Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator') { + $driver->setLocator(new DefaultFileLocator($locator->getPaths(), $options->getExtension())); + } else { + throw new InvalidArgumentException(sprintf( + 'Discovered file locator for driver of type "%s" is an instance of "%s". This factory ' + . 'supports only the DefaultFileLocator when an extension is set for the file locator', + get_class($driver), + get_class($locator) + )); + } + } + + // Extra post-create options for DriverChain. + if ($driver instanceof MappingDriverChain && $options->getDrivers()) { + /* @var $driver \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain */ + $drivers = $options->getDrivers(); + + if (!is_array($drivers)) { + $drivers = array($drivers); + } + + foreach ($drivers as $namespace => $driverName) { + $options = $this->getOptions($sl, 'driver', $driverName); + $driver->addDriver($this->createDriver($sl, $options), $namespace); + } + } + + return $driver; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/EventManagerFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/EventManagerFactory.php new file mode 100644 index 00000000..054adbea --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/EventManagerFactory.php @@ -0,0 +1,79 @@ +. + */ + +namespace DoctrineModule\Service; + +use InvalidArgumentException; +use Doctrine\Common\EventManager; +use Doctrine\Common\EventSubscriber; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Factory responsible for creating EventManager instances + */ +class EventManagerFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + */ + public function createService(ServiceLocatorInterface $sl) + { + /** @var $options \DoctrineModule\Options\EventManager */ + $options = $this->getOptions($sl, 'eventmanager'); + $eventManager = new EventManager(); + + foreach ($options->getSubscribers() as $subscriberName) { + $subscriber = $subscriberName; + + if (is_string($subscriber)) { + if ($sl->has($subscriber)) { + $subscriber = $sl->get($subscriber); + } elseif (class_exists($subscriber)) { + $subscriber = new $subscriber(); + } + } + + if ($subscriber instanceof EventSubscriber) { + $eventManager->addEventSubscriber($subscriber); + continue; + } + + throw new InvalidArgumentException(sprintf( + 'Invalid event subscriber "%s" given, must be a service name, ' + . 'class name or an instance implementing Doctrine\Common\EventSubscriber', + is_object($subscriberName) + ? get_class($subscriberName) + : (is_string($subscriberName) ? $subscriberName : gettype($subscriber)) + )); + } + + return $eventManager; + } + + /** + * Get the class name of the options associated with this factory. + * + * @return string + */ + public function getOptionsClass() + { + return 'DoctrineModule\Options\EventManager'; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/ZendStorageCacheFactory.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/ZendStorageCacheFactory.php new file mode 100644 index 00000000..2a1cea59 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Service/ZendStorageCacheFactory.php @@ -0,0 +1,64 @@ +. + */ + +namespace DoctrineModule\Service; + +use RuntimeException; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Cache\Storage\StorageInterface; +use DoctrineModule\Cache\ZendStorageCache; + +/** + * ZendStorageCache ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ZendStorageCacheFactory extends CacheFactory +{ + /** + * {@inheritDoc} + * @return ZendStorageCache + * @throws RuntimeException + */ + public function createService(ServiceLocatorInterface $sl) + { + /** @var $options \DoctrineModule\Options\Cache */ + $options = $this->getOptions($sl, 'cache'); + $instance = $options->getInstance(); + + if (!$instance) { + // @todo move this validation to the options class + throw new RuntimeException('ZendStorageCache must have a referenced cache instance'); + } + + $cache = $sl->get($instance); + + if (!$cache instanceof StorageInterface) { + throw new RuntimeException(sprintf( + 'Retrieved storage "%s" is not a Zend\Cache\Storage\StorageInterface instance, %s found', + $instance, + is_object($cache) ? get_class($cache) : getType($cache) + )); + } + + return new ZendStorageCache($cache); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php new file mode 100644 index 00000000..be033b88 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php @@ -0,0 +1,435 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator; + +use DateTime; +use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy; +use InvalidArgumentException; +use RuntimeException; +use Traversable; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Zend\Stdlib\Hydrator\AbstractHydrator; +use Zend\Stdlib\Hydrator\Strategy\StrategyInterface; + +/** + * This hydrator has been completely refactored for DoctrineModule 0.7.0. It provides an easy and powerful way + * of extracting/hydrator objects in Doctrine, by handling most associations types. + * + * Note that now a hydrator is bound to a specific entity (while more standard hydrators can be instanciated once + * and be used with objects of different types). Most of the time, this won't be a problem as in a form we only + * create one hydrator. This is by design, because this hydrator uses metadata extensively, so it's more efficient + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +class DoctrineObject extends AbstractHydrator +{ + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var ClassMetadata + */ + protected $metadata; + + /** + * @var bool + */ + protected $byValue = true; + + + /** + * Constructor + * + * @param ObjectManager $objectManager The ObjectManager to use + * @param string $targetClass The FQCN of the hydrated/extracted object + * @param bool $byValue If set to true, hydrator will always use entity's public API + */ + public function __construct(ObjectManager $objectManager, $targetClass, $byValue = true) + { + parent::__construct(); + + $this->objectManager = $objectManager; + $this->metadata = $objectManager->getClassMetadata($targetClass); + $this->byValue = (bool) $byValue; + + $this->prepare(); + } + + /** + * Extract values from an object + * + * @param object $object + * @return array + */ + public function extract($object) + { + if ($this->byValue) { + return $this->extractByValue($object); + } + + return $this->extractByReference($object); + } + + /** + * Hydrate $object with the provided $data. + * + * @param array $data + * @param object $object + * @return object + */ + public function hydrate(array $data, $object) + { + if ($this->byValue) { + return $this->hydrateByValue($data, $object); + } + + return $this->hydrateByReference($data, $object); + } + + /** + * {@inheritDoc} + * @throws InvalidArgumentException If a strategy added to a collection does not extend AbstractCollectionStrategy + */ + public function addStrategy($name, StrategyInterface $strategy) + { + if ($this->metadata->hasAssociation($name)) { + if (!$strategy instanceof Strategy\AbstractCollectionStrategy) { + throw new InvalidArgumentException( + sprintf( + 'Strategies used for collections valued associations must inherit from ' + . 'Strategy\AbstractCollectionStrategy, %s given', + get_class($strategy) + ) + ); + } + + $strategy->setCollectionName($name) + ->setClassMetadata($this->metadata); + } + + return parent::addStrategy($name, $strategy); + } + + /** + * Prepare the hydrator by adding strategies to every collection valued associations + * + * @return void + */ + protected function prepare() + { + $metadata = $this->metadata; + $associations = $metadata->getAssociationNames(); + + foreach ($associations as $association) { + // We only need to prepare collection valued associations + if ($metadata->isCollectionValuedAssociation($association)) { + if ($this->byValue) { + $this->addStrategy($association, new Strategy\AllowRemoveByValue()); + } else { + $this->addStrategy($association, new Strategy\AllowRemoveByReference()); + } + } + } + } + + /** + * Extract values from an object using a by-value logic (this means that it uses the entity + * API, in this case, getters) + * + * @param object $object + * @throws RuntimeException + * @return array + */ + protected function extractByValue($object) + { + $fieldNames = array_merge($this->metadata->getFieldNames(), $this->metadata->getAssociationNames()); + $methods = get_class_methods($object); + + $data = array(); + foreach ($fieldNames as $fieldName) { + $getter = 'get' . ucfirst($fieldName); + + // Ignore unknown fields + if (!in_array($getter, $methods)) { + continue; + } + + $data[$fieldName] = $this->extractValue($fieldName, $object->$getter()); + } + + return $data; + } + + /** + * Extract values from an object using a by-reference logic (this means that values are + * directly fetched without using the public API of the entity, in this case, getters) + * + * @param object $object + * @return array + */ + protected function extractByReference($object) + { + $fieldNames = array_merge($this->metadata->getFieldNames(), $this->metadata->getAssociationNames()); + $refl = $this->metadata->getReflectionClass(); + + $data = array(); + foreach ($fieldNames as $fieldName) { + $reflProperty = $refl->getProperty($fieldName); + $reflProperty->setAccessible(true); + + $data[$fieldName] = $this->extractValue($fieldName, $reflProperty->getValue($object)); + } + + return $data; + } + + /** + * Hydrate the object using a by-value logic (this means that it uses the entity API, in this + * case, setters) + * + * @param array $data + * @param object $object + * @throws RuntimeException + * @return object + */ + protected function hydrateByValue(array $data, $object) + { + $object = $this->tryConvertArrayToObject($data, $object); + $metadata = $this->metadata; + + foreach ($data as $field => $value) { + $value = $this->handleTypeConversions($value, $metadata->getTypeOfField($field)); + $setter = 'set' . ucfirst($field); + + if ($metadata->hasAssociation($field)) { + $target = $metadata->getAssociationTargetClass($field); + + if ($metadata->isSingleValuedAssociation($field)) { + if (!method_exists($object, $setter)) { + continue; + } + + $value = $this->toOne($target, $this->hydrateValue($field, $value)); + $object->$setter($value); + } elseif ($metadata->isCollectionValuedAssociation($field)) { + $this->toMany($object, $field, $target, $value); + } + } else { + if (!method_exists($object, $setter)) { + continue; + } + + $object->$setter($value); + } + } + + return $object; + } + + /** + * Hydrate the object using a by-reference logic (this means that values are modified directly without + * using the public API, in this case setters, and hence override any logic that could be done in those + * setters) + * + * @param array $data + * @param object $object + * @return object + */ + protected function hydrateByReference(array $data, $object) + { + $object = $this->tryConvertArrayToObject($data, $object); + $metadata = $this->metadata; + $refl = $metadata->getReflectionClass(); + + foreach ($data as $field => $value) { + // Ignore unknown fields + if (!$refl->hasProperty($field)) { + continue; + } + + $value = $this->handleTypeConversions($value, $metadata->getTypeOfField($field)); + $reflProperty = $refl->getProperty($field); + $reflProperty->setAccessible(true); + + if ($metadata->hasAssociation($field)) { + $target = $metadata->getAssociationTargetClass($field); + + if ($metadata->isSingleValuedAssociation($field)) { + $value = $this->toOne($target, $this->hydrateValue($field, $value)); + $reflProperty->setValue($object, $value); + } elseif ($metadata->isCollectionValuedAssociation($field)) { + $this->toMany($object, $field, $target, $value); + } + } else { + $reflProperty->setValue($object, $value); + } + } + + return $object; + } + + /** + * This function tries, given an array of data, to convert it to an object if the given array contains + * an identifier for the object. This is useful in a context of updating existing entities, without ugly + * tricks like setting manually the existing id directly into the entity + * + * @param array $data The data that may contain identifiers keys + * @param object $object + * @return object + */ + protected function tryConvertArrayToObject($data, $object) + { + $metadata = $this->metadata; + $identifierNames = $metadata->getIdentifierFieldNames($object); + $identifierValues = array(); + + if (empty($identifierNames)) { + return $object; + } + + foreach ($identifierNames as $identifierName) { + if (!isset($data[$identifierName]) || empty($data[$identifierName])) { + return $object; + } + + $identifierValues[$identifierName] = $data[$identifierName]; + } + + return $this->find($identifierValues, $metadata->getName()); + } + + /** + * Handle ToOne associations + * + * @param string $target + * @param mixed $value + * @return object + */ + protected function toOne($target, $value) + { + if ($value instanceof $target) { + return $value; + } + + return $this->find($value, $target); + } + + /** + * Handle ToMany associations. In proper Doctrine design, Collections should not be swapped, so + * collections are always handled by reference. Internally, every collection is handled using specials + * strategies that inherit from AbstractCollectionStrategy class, and that add or remove elements but without + * changing the collection of the object + * + * @param object $object + * @param mixed $collectionName + * @param string $target + * @param mixed $values + * @return void + */ + protected function toMany($object, $collectionName, $target, $values) + { + if (!is_array($values) && !$values instanceof Traversable) { + $values = (array) $values; + } + + $collection = array(); + + // If the collection contains identifiers, fetch the objects from database + foreach ($values as $value) { + if ($value instanceof $target) { + $collection[] = $value; + } elseif ($value !== null) { + $targetObject = $this->find($value, $target); + + if ($targetObject !== null) { + $collection[] = $targetObject; + } + } + } + + // Set the object so that the strategy can extract the Collection from it + $collectionStrategy = $this->getStrategy($collectionName); + + // Even if this check is applied in addStrategy, subclasses may inject invalid strategies + if ( ! $collectionStrategy instanceof AbstractCollectionStrategy) { + throw new InvalidArgumentException( + sprintf( + 'Strategies used for collections valued associations must inherit from ' + . 'Strategy\AbstractCollectionStrategy, %s given', + get_class($collectionStrategy) + ) + ); + } + + $collectionStrategy->setObject($object); + + // We could directly call hydrate method from the strategy, but if people want to override + // hydrateValue function, they can do it and do their own stuff + $this->hydrateValue($collectionName, $collection); + } + + /** + * Handle various type conversions that should be supported natively by Doctrine (like DateTime) + * + * @param mixed $value + * @param string $typeOfField + * @return DateTime + */ + protected function handleTypeConversions($value, $typeOfField) + { + switch($typeOfField) { + case 'datetime': + case 'time': + case 'date': + if (is_int($value)) { + $dateTime = new DateTime(); + $dateTime->setTimestamp($value); + $value = $dateTime; + } elseif (is_string($value)) { + $value = new DateTime($value); + } + + break; + default: + } + + return $value; + } + + /** + * Find an object by its identifiers + * + * @param mixed $identifiers + * @param string $targetClass + * + * @return object|null + */ + protected function find($identifiers, $targetClass) + { + return $this->objectManager->find($targetClass, $identifiers); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AbstractCollectionStrategy.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AbstractCollectionStrategy.php new file mode 100644 index 00000000..0b805f9f --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AbstractCollectionStrategy.php @@ -0,0 +1,183 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator\Strategy; + +use InvalidArgumentException; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Zend\Stdlib\Hydrator\Strategy\StrategyInterface; + +/** + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +abstract class AbstractCollectionStrategy implements StrategyInterface +{ + /** + * @var string + */ + protected $collectionName; + + /** + * @var ClassMetadata + */ + protected $metadata; + + /** + * @var object + */ + protected $object; + + + /** + * Set the name of the collection + * + * @param string $collectionName + * @return AbstractCollectionStrategy + */ + public function setCollectionName($collectionName) + { + $this->collectionName = (string) $collectionName; + return $this; + } + + /** + * Get the name of the collection + * + * @return string + */ + public function getCollectionName() + { + return $this->collectionName; + } + + /** + * Set the class metadata + * + * @param ClassMetadata $classMetadata + * @return AbstractCollectionStrategy + */ + public function setClassMetadata(ClassMetadata $classMetadata) + { + $this->metadata = $classMetadata; + return $this; + } + + /** + * Get the class metadata + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->metadata; + } + + /** + * Set the object + * + * @param object $object + * @return AbstractCollectionStrategy + */ + public function setObject($object) + { + if (!is_object($object)) { + throw new InvalidArgumentException(sprintf( + 'The parameter given to setObject method of %s class is not an object', + get_called_class() + )); + } + + $this->object = $object; + return $this; + } + + /** + * Get the object + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * {@inheritDoc} + */ + public function extract($value) + { + return $value; + } + + /** + * Return the collection by value (using the public API) + * + * @return Collection + */ + protected function getCollectionFromObjectByValue() + { + $object = $this->getObject(); + $getter = 'get' . ucfirst($this->getCollectionName()); + + if (!method_exists($object, $getter)) { + throw new InvalidArgumentException(sprintf( + 'The getter %s to access collection %s in object %s does not exist', + $getter, + $this->getCollectionName(), + get_class($object) + )); + } + + return $object->$getter(); + } + + /** + * Return the collection by reference (not using the public API) + * + * @return Collection + */ + protected function getCollectionFromObjectByReference() + { + $object = $this->getObject(); + $refl = $this->getClassMetadata()->getReflectionClass(); + $reflProperty = $refl->getProperty($this->getCollectionName()); + + $reflProperty->setAccessible(true); + + return $reflProperty->getValue($object); + } + + + /** + * This method is used internally by array_udiff to check if two objects are equal, according to their + * SPL hash. This is needed because the native array_diff only compare strings + * + * @param object $a + * @param object $b + */ + protected function compareObjects($a, $b) + { + return strcmp(spl_object_hash($a), spl_object_hash($b)); + } +} + diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByReference.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByReference.php new file mode 100644 index 00000000..9278e4c2 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByReference.php @@ -0,0 +1,58 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator\Strategy; + +/** + * When this strategy is used for Collections, if the new collection does not contain elements that are present in + * the original collection, then this strategy remove elements from the original collection. For instance, if the + * collection initially contains elements A and B, and that the new collection contains elements B and C, then the + * final collection will contain elements B and C (while element A will be asked to be removed). + * + * This strategy is by reference, this means it won't use public API to add/remove elements to the collection + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +class AllowRemoveByReference extends AbstractCollectionStrategy +{ + /** + * {@inheritDoc} + */ + public function hydrate($value) + { + $collection = $this->getCollectionFromObjectByReference(); + $collectionArray = $collection->toArray(); + + $toAdd = array_udiff($value, $collectionArray, array($this, 'compareObjects')); + $toRemove = array_udiff($collectionArray, $value, array($this, 'compareObjects')); + + foreach ($toAdd as $element) { + $collection->add($element); + } + + foreach ($toRemove as $element) { + $collection->removeElement($element); + } + + return $collection; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByValue.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByValue.php new file mode 100644 index 00000000..1e23e496 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/AllowRemoveByValue.php @@ -0,0 +1,66 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator\Strategy; + +use LogicException; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * When this strategy is used for Collections, if the new collection does not contain elements that are present in + * the original collection, then this strategy remove elements from the original collection. For instance, if the + * collection initially contains elements A and B, and that the new collection contains elements B and C, then the + * final collection will contain elements B and C (while element A will be asked to be removed). + * + * This strategy is by value, this means it will use the public API (in this case, adder and remover) + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +class AllowRemoveByValue extends AbstractCollectionStrategy +{ + /** + * {@inheritDoc} + */ + public function hydrate($value) + { + // AllowRemove strategy need "adder" and "remover" + $adder = 'add' . ucfirst($this->collectionName); + $remover = 'remove' . ucfirst($this->collectionName); + + if (!method_exists($this->object, $adder) || !method_exists($this->object, $remover)) { + throw new LogicException(sprintf( + 'AllowRemove strategy for DoctrineModule hydrator requires both %s and %s to be defined in %s + entity domain code, but one or both seem to be missing', + $adder, $remover, get_class($this->object) + )); + } + + $collection = $this->getCollectionFromObjectByValue()->toArray(); + $toAdd = new ArrayCollection(array_udiff($value, $collection, array($this, 'compareObjects'))); + $toRemove = new ArrayCollection(array_udiff($collection, $value, array($this, 'compareObjects'))); + + $this->object->$adder($toAdd); + $this->object->$remover($toRemove); + + return $collection; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByReference.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByReference.php new file mode 100644 index 00000000..77b017e4 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByReference.php @@ -0,0 +1,53 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator\Strategy; + +/** + * When this strategy is used for Collections, if the new collection does not contain elements that are present in + * the original collection, then this strategy will not remove those elements. At most, it will add new elements. For + * instance, if the collection initially contains elements A and B, and that the new collection contains elements B + * and C, then the final collection will contain elements A, B and C. + * + * This strategy is by reference, this means it won't use the public API to remove elements + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +class DisallowRemoveByReference extends AbstractCollectionStrategy +{ + /** + * {@inheritDoc} + */ + public function hydrate($value) + { + $collection = $this->getCollectionFromObjectByReference(); + $collectionArray = $collection->toArray(); + + $toAdd = array_udiff($value, $collectionArray, array($this, 'compareObjects')); + + foreach ($toAdd as $element) { + $collection->add($element); + } + + return $collection; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByValue.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByValue.php new file mode 100644 index 00000000..a074b995 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/Strategy/DisallowRemoveByValue.php @@ -0,0 +1,63 @@ +. + */ + +namespace DoctrineModule\Stdlib\Hydrator\Strategy; + +use LogicException; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * When this strategy is used for Collections, if the new collection does not contain elements that are present in + * the original collection, then this strategy will not remove those elements. At most, it will add new elements. For + * instance, if the collection initially contains elements A and B, and that the new collection contains elements B + * and C, then the final collection will contain elements A, B and C. + * + * This strategy is by value, this means it will use the public API (in this case, remover) + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.7.0 + * @author Michael Gallego + */ +class DisallowRemoveByValue extends AbstractCollectionStrategy +{ + /** + * {@inheritDoc} + */ + public function hydrate($value) + { + // AllowRemove strategy need "adder" + $adder = 'add' . ucfirst($this->collectionName); + + if (!method_exists($this->object, $adder)) { + throw new LogicException(sprintf( + 'AllowRemove strategy for DoctrineModule hydrator requires %s to be defined in %s + entity domain code, but it seems to be missing', + $adder, get_class($this->object) + )); + } + + $collection = $this->getCollectionFromObjectByValue()->toArray(); + $toAdd = new ArrayCollection(array_udiff($value, $collection, array($this, 'compareObjects'))); + + $this->object->$adder($toAdd); + + return $collection; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/NoObjectExists.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/NoObjectExists.php new file mode 100644 index 00000000..f8595259 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/NoObjectExists.php @@ -0,0 +1,60 @@ +. + */ + +namespace DoctrineModule\Validator; + +/** + * Class that validates if objects does not exist in a given repository with a given list of matched fields + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.4.0 + * @author Marco Pivetta + */ +class NoObjectExists extends ObjectExists +{ + /** + * Error constants + */ + const ERROR_OBJECT_FOUND = 'objectFound'; + + /** + * @var array Message templates + */ + protected $messageTemplates = array( + self::ERROR_OBJECT_FOUND => "An object matching '%value%' was found", + ); + + /** + * {@inheritDoc} + */ + public function isValid($value) + { + $value = $this->cleanSearchValue($value); + $match = $this->objectRepository->findOneBy($value); + + if (is_object($match)) { + $this->error(self::ERROR_OBJECT_FOUND, $value); + + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/ObjectExists.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/ObjectExists.php new file mode 100644 index 00000000..cd83160d --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/ObjectExists.php @@ -0,0 +1,188 @@ +. + */ + +namespace DoctrineModule\Validator; + +use Zend\Validator\AbstractValidator; +use Zend\Validator\Exception; +use Doctrine\Common\Persistence\ObjectRepository; +use Zend\Stdlib\ArrayUtils; + +/** + * Class that validates if objects exist in a given repository with a given list of matched fields + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.4.0 + * @author Marco Pivetta + */ +class ObjectExists extends AbstractValidator +{ + /** + * Error constants + */ + const ERROR_NO_OBJECT_FOUND = 'noObjectFound'; + + /** + * @var array Message templates + */ + protected $messageTemplates = array( + self::ERROR_NO_OBJECT_FOUND => "No object matching '%value%' was found", + ); + + /** + * ObjectRepository from which to search for entities + * + * @var ObjectRepository + */ + protected $objectRepository; + + /** + * Fields to be checked + * + * @var array + */ + protected $fields; + + /** + * Constructor + * + * @param array $options required keys are `object_repository`, which must be an instance of + * Doctrine\Common\Persistence\ObjectRepository, and `fields`, with either + * a string or an array of strings representing the fields to be matched by the validator. + * @throws \Zend\Validator\Exception\InvalidArgumentException + */ + public function __construct(array $options) + { + if (!isset($options['object_repository']) || !$options['object_repository'] instanceof ObjectRepository) { + if (!array_key_exists('object_repository', $options)) { + $provided = 'nothing'; + } else { + if (is_object($options['object_repository'])) { + $provided = get_class($options['object_repository']); + } else { + $provided = getType($options['object_repository']); + } + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Option "object_repository" is required and must be an instance of' + . ' Doctrine\Common\Persistence\ObjectRepository, %s given', + $provided + )); + } + + $this->objectRepository = $options['object_repository']; + + if (!isset($options['fields'])) { + throw new Exception\InvalidArgumentException( + 'Key `fields` must be provided and be a field or a list of fields to be used when searching for' + . ' existing instances' + ); + } + + $this->fields = $options['fields']; + $this->validateFields(); + + parent::__construct($options); + } + + /** + * Filters and validates the fields passed to the constructor + * + * @throws \Zend\Validator\Exception\InvalidArgumentException + * @return array + */ + private function validateFields() + { + $fields = (array) $this->fields; + + if (empty($fields)) { + throw new Exception\InvalidArgumentException('Provided fields list was empty!'); + } + + foreach ($fields as $key => $field) { + if (!is_string($field)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Provided fields must be strings, %s provided for key %s', + gettype($field), + $key + )); + } + } + + $this->fields = array_values($fields); + } + + /** + * @param string|array $value a field value or an array of field values if more fields have been configured to be + * matched + * @return array + * @throws \Zend\Validator\Exception\RuntimeException + */ + protected function cleanSearchValue($value) + { + $value = (array) $value; + + if (ArrayUtils::isHashTable($value)) { + $matchedFieldsValues = array(); + + foreach ($this->fields as $field) { + if (!array_key_exists($field, $value)) { + throw new Exception\RuntimeException(sprintf( + 'Field "%s" was not provided, but was expected since the configured field lists needs' + . ' it for validation', + $field + )); + } + + $matchedFieldsValues[$field] = $value[$field]; + } + } else { + $matchedFieldsValues = @array_combine($this->fields, $value); + + if (false === $matchedFieldsValues) { + throw new Exception\RuntimeException(sprintf( + 'Provided values count is %s, while expected number of fields to be matched is %s', + count($value), + count($this->fields) + )); + } + } + + return $matchedFieldsValues; + } + + /** + * {@inheritDoc} + */ + public function isValid($value) + { + $value = $this->cleanSearchValue($value); + $match = $this->objectRepository->findOneBy($value); + + if (is_object($match)) { + return true; + } + + $this->error(self::ERROR_NO_OBJECT_FOUND, $value); + + return false; + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/UniqueObject.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/UniqueObject.php new file mode 100644 index 00000000..f516d19d --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Validator/UniqueObject.php @@ -0,0 +1,166 @@ +. + */ + +namespace DoctrineModule\Validator; + +use Doctrine\Common\Persistence\ObjectManager; +use Zend\Validator\Exception; + +/** + * Class that validates if objects exist in a given repository with a given list of matched fields only once. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Oskar Bley + */ +class UniqueObject extends ObjectExists +{ + /** + * Error constants + */ + const ERROR_OBJECT_NOT_UNIQUE = 'objectNotUnique'; + + /** + * @var array Message templates + */ + protected $messageTemplates = array( + self::ERROR_OBJECT_NOT_UNIQUE => "There is already another object matching '%value%'", + ); + + /** + * @var ObjectManager + */ + protected $objectManager; + + /*** + * Constructor + * + * @param array $options required keys are `object_repository`, which must be an instance of + * Doctrine\Common\Persistence\ObjectRepository, `object_manager`, which + * must be an instance of Doctrine\Common\Persistence\ObjectManager, + * and `fields`, with either a string or an array of strings representing + * the fields to be matched by the validator. + * @throws Exception\InvalidArgumentException + */ + public function __construct(array $options) + { + parent::__construct($options); + + if (!isset($options['object_manager']) || !$options['object_manager'] instanceof ObjectManager) { + if (!array_key_exists('object_manager', $options)) { + $provided = 'nothing'; + } else { + if (is_object($options['object_manager'])) { + $provided = get_class($options['object_manager']); + } else { + $provided = getType($options['object_manager']); + } + } + + throw new Exception\InvalidArgumentException( + sprintf( + 'Option "object_manager" is required and must be an instance of' + . ' Doctrine\Common\Persistence\ObjectManager, %s given', + $provided + ) + ); + } + + $this->objectManager = $options['object_manager']; + } + + /** + * Returns false if there is another object with the same field values but other identifiers. + * + * @param mixed $value + * @param array $context + * @return boolean + */ + public function isValid($value, $context = null) + { + $value = $this->cleanSearchValue($value); + $match = $this->objectRepository->findOneBy($value); + + if (!is_object($match)) { + return true; + } + + $expectedIdentifiers = $this->getExpectedIdentifiers($context); + $foundIdentifiers = $this->getFoundIdentifiers($match); + + if (count(array_diff_assoc($expectedIdentifiers, $foundIdentifiers)) == 0) { + return true; + } + + $this->error(self::ERROR_OBJECT_NOT_UNIQUE, $value); + return false; + } + + /** + * Gets the identifiers from the matched object. + * + * @param object $match + * @return array + * @throws Exception\RuntimeException + */ + protected function getFoundIdentifiers($match) + { + return $this->objectManager + ->getClassMetadata($this->objectRepository->getClassName()) + ->getIdentifierValues($match); + } + + /** + * Gets the identifiers from the context. + * + * @param array $context + * @return array + * @throws Exception\RuntimeException + */ + protected function getExpectedIdentifiers(array $context = null) + { + if ($context === null) { + throw new Exception\RuntimeException( + 'Expected context to be an array but is null' + ); + } + + $result = array(); + foreach ($this->getIdentifiers() as $identifierField) { + + if (!isset($context[$identifierField])) { + throw new Exception\RuntimeException(\sprintf('Expected context to contain %s', $identifierField)); + } + + $result[$identifierField] = $context[$identifierField]; + } + return $result; + } + + + /** + * @return array the names of the identifiers + */ + protected function getIdentifiers() + { + return $this->objectManager + ->getClassMetadata($this->objectRepository->getClassName()) + ->getIdentifierFieldNames(); + } +} diff --git a/vendor/doctrine/doctrine-module/src/DoctrineModule/Version.php b/vendor/doctrine/doctrine-module/src/DoctrineModule/Version.php new file mode 100644 index 00000000..6cf2e842 --- /dev/null +++ b/vendor/doctrine/doctrine-module/src/DoctrineModule/Version.php @@ -0,0 +1,33 @@ +. + */ + +namespace DoctrineModule; + +/** + * Version + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Kyle Spraggs + */ +class Version +{ + const VERSION = '0.5.0'; +} diff --git a/vendor/doctrine/doctrine-module/tests/.gitignore b/vendor/doctrine/doctrine-module/tests/.gitignore new file mode 100644 index 00000000..ba33e471 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/.gitignore @@ -0,0 +1 @@ +TestConfiguration.php diff --git a/vendor/doctrine/doctrine-module/tests/Bootstrap.php b/vendor/doctrine/doctrine-module/tests/Bootstrap.php new file mode 100644 index 00000000..6f73936e --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/Bootstrap.php @@ -0,0 +1,39 @@ +. + */ + +use DoctrineModuleTest\ServiceManagerTestCase; + +chdir(__DIR__); + +if ( + ! ($loader = @include __DIR__ . '/../vendor/autoload.php') + && ! ($loader = @include __DIR__ . '/../vendor/autoload.php') +) { + throw new RuntimeException('vendor/autoload.php could not be found. Run composer installation'); +} + +$loader->add('DoctrineModuleTest\\', __DIR__); + +if (!$config = @include __DIR__ . '/TestConfiguration.php') { + $config = require __DIR__ . '/TestConfiguration.php.dist'; +} + +ServiceManagerTestCase::setServiceManagerConfiguration( + isset($configuration['service_manager']) ? $configuration['service_manager'] : array() +); diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/ObjectRepositoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/ObjectRepositoryTest.php new file mode 100644 index 00000000..8f4f3bf2 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/ObjectRepositoryTest.php @@ -0,0 +1,294 @@ +. + */ + +namespace DoctrineModuleTest\Authentication\Adapter; + +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Authentication\Adapter\ObjectRepository as ObjectRepositoryAdapter; +use DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject; +use DoctrineModuleTest\Authentication\Adapter\TestAsset\PublicPropertiesIdentityObject; + +/** + * Tests for the ObjectRepository based authentication adapter + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ObjectRepositoryTest extends BaseTestCase +{ + public function testWillRejectInvalidIdentityProperty() + { + $this->setExpectedException( + 'Zend\Authentication\Adapter\Exception\InvalidArgumentException', + 'Provided $identityProperty is invalid, boolean given' + ); + new ObjectRepositoryAdapter(array( + 'identity_property' => false + ) + ); + } + + public function testWillRejectInvalidCredentialProperty() + { + $this->setExpectedException( + 'Zend\Authentication\Adapter\Exception\InvalidArgumentException', + 'Provided $credentialProperty is invalid, boolean given' + ); + new ObjectRepositoryAdapter(array( + 'credential_property' => false + ) + ); + } + + public function testWillRequireIdentityValue() + { + $this->setExpectedException( + 'Zend\Authentication\Adapter\Exception\RuntimeException', + 'A value for the identity was not provided prior to authentication with ObjectRepository authentication ' + . 'adapter' + ); + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_manager' => $this->getMock('Doctrine\Common\Persistence\ObjectManager'), + 'identity_class' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + )); + $adapter->setCredentialValue('a credetential'); + $adapter->authenticate(); + } + + public function testWillRequireCredentialValue() + { + $this->setExpectedException( + 'Zend\Authentication\Adapter\Exception\RuntimeException', + 'A credential value was not provided prior to authentication with ObjectRepository authentication adapter' + ); + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_manager' => $this->getMock('Doctrine\Common\Persistence\ObjectManager'), + 'identity_class' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + )); + + $adapter->setIdentityValue('an identity'); + $adapter->authenticate(); + } + + public function testWillRejectInvalidCredentialCallable() + { + $this->setExpectedException( + 'Zend\Authentication\Adapter\Exception\InvalidArgumentException', + '"array" is not a callable' + ); + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_manager' => $this->getMock('Doctrine\Common\Persistence\ObjectManager'), + 'identity_class' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + 'credential_callable' => array() + )); + + $adapter->authenticate(); + } + + public function testAuthentication() + { + $entity = new IdentityObject(); + $entity->setUsername('a username'); + $entity->setPassword('a password'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $method = $objectRepository + ->expects($this->exactly(2)) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'a username'))) + ->will($this->returnValue($entity)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->exactly(2)) + ->method('getRepository') + ->with($this->equalTo('DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject')) + ->will($this->returnValue($objectRepository)); + + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_manager' => $objectManager, + 'identity_class' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + 'credential_property' => 'password', + 'identity_property' => 'username' + )); + + $adapter->setIdentityValue('a username'); + $adapter->setCredentialValue('a password'); + + $result = $adapter->authenticate(); + + $this->assertTrue($result->isValid()); + $this->assertInstanceOf('DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', $result->getIdentity()); + + $method->will($this->returnValue(null)); + + $result = $adapter->authenticate(); + + $this->assertFalse($result->isValid()); + } + + public function testAuthenticationWithPublicProperties() + { + $entity = new PublicPropertiesIdentityObject(); + $entity->username = 'a username'; + $entity->password = 'a password'; + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $method = $objectRepository + ->expects($this->exactly(2)) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'a username'))) + ->will($this->returnValue($entity)); + + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_repository' => $objectRepository, + 'credential_property' => 'password', + 'identity_property' => 'username' + )); + + $adapter->setIdentityValue('a username'); + $adapter->setCredentialValue('a password'); + + $result = $adapter->authenticate(); + + $this->assertTrue($result->isValid()); + + $method->will($this->returnValue(null)); + + $result = $adapter->authenticate(); + + $this->assertFalse($result->isValid()); + } + + public function testWillRefuseToAuthenticateWithoutGettersOrPublicMethods() + { + $this->setExpectedException('Zend\Authentication\Adapter\Exception\UnexpectedValueException'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository + ->expects($this->once()) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'a username'))) + ->will($this->returnValue(new \stdClass())); + + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_repository' => $objectRepository, + 'credential_property' => 'password', + 'identity_property' => 'username' + )); + + $adapter->setIdentityValue('a username'); + $adapter->setCredentialValue('a password'); + $adapter->authenticate(); + } + + public function testCanValidateWithSpecialCrypt() + { + $hash = '$2y$07$usesomesillystringforsalt$'; + $entity = new IdentityObject(); + $entity->setUsername('username'); + // Crypt password using Blowfish + $entity->setPassword(crypt('password', $hash)); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository + ->expects($this->exactly(2)) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'username'))) + ->will($this->returnValue($entity)); + + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_repository' => $objectRepository, + 'credential_property' => 'password', + 'identity_property' => 'username', + // enforced type hinting to verify that closure is invoked correctly + 'credential_callable' => function(IdentityObject $identity, $credentialValue) use ($hash) { + return $identity->getPassword() === crypt($credentialValue, $hash); + } + )); + + $adapter->setIdentityValue('username'); + $adapter->setCredentialValue('password'); + + $result = $adapter->authenticate(); + + $this->assertTrue($result->isValid()); + + $adapter->setCredentialValue('wrong password'); + $result = $adapter->authenticate(); + + $this->assertFalse($result->isValid()); + } + + public function testWillRefuseToAuthenticateWhenInvalidInstanceIsFound() + { + $this->setExpectedException('Zend\Authentication\Adapter\Exception\UnexpectedValueException'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository + ->expects($this->once()) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'a username'))) + ->will($this->returnValue(new \stdClass())); + + $adapter = new ObjectRepositoryAdapter(); + $adapter->setOptions(array( + 'object_repository' => $objectRepository, + 'credential_property' => 'password', + 'identity_property' => 'username' + )); + + $adapter->setIdentityValue('a username'); + $adapter->setCredentialValue('a password'); + + $adapter->authenticate(); + } + + public function testWillNotCastAuthCredentialValue() + { + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $adapter = new ObjectRepositoryAdapter(); + $entity = new IdentityObject(); + + $entity->setPassword(0); + $adapter->setOptions( + array( + 'object_repository' => $objectRepository, + 'credential_property' => 'password', + 'identity_property' => 'username' + ) + ); + $adapter->setIdentityValue('a username'); + $adapter->setCredentialValue('00000'); + $objectRepository + ->expects($this->once()) + ->method('findOneBy') + ->with($this->equalTo(array('username' => 'a username'))) + ->will($this->returnValue($entity)); + + $this->assertFalse($adapter->authenticate()->isValid()); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/IdentityObject.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/IdentityObject.php new file mode 100644 index 00000000..74c4388b --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/IdentityObject.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineModuleTest\Authentication\Adapter\TestAsset; + +/** + * Simple mock object for authentication adapter tests + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class IdentityObject +{ + /** + * @var string|null + */ + protected $username; + + /** + * @var string|null + */ + protected $password; + + /** + * @param string $password + */ + public function setPassword($password) + { + $this->password = (string) $password; + } + + /** + * @return string|null + */ + public function getPassword() + { + return $this->password; + } + + /** + * @param string $username + */ + public function setUsername($username) + { + $this->username = (string) $username; + } + + /** + * @return string|null + */ + public function getUsername() + { + return $this->username; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/PublicPropertiesIdentityObject.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/PublicPropertiesIdentityObject.php new file mode 100644 index 00000000..85acf41e --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Adapter/TestAsset/PublicPropertiesIdentityObject.php @@ -0,0 +1,40 @@ +. + */ + +namespace DoctrineModuleTest\Authentication\Adapter\TestAsset; + +/** + * Simple mock object for authentication adapter tests with direct property access + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class PublicPropertiesIdentityObject +{ + /** + * @var string|null + */ + public $username; + + /** + * @var string|null + */ + public $password; +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Storage/ObjectRepositoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Storage/ObjectRepositoryTest.php new file mode 100644 index 00000000..a759a1cd --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Authentication/Storage/ObjectRepositoryTest.php @@ -0,0 +1,70 @@ +. + */ + +namespace DoctrineModuleTest\Authentication\Storage; + +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Authentication\Storage\ObjectRepository as ObjectRepositoryStorage; +use DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject; +use Zend\Authentication\Storage\NonPersistent as NonPersistentStorage; + +/** + * Tests for the ObjectRepository based authentication adapter + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ObjectRepositoryTest extends BaseTestCase +{ + public function testCanRetrieveEntityFromObjectRepositoryStorage() + { + // Identifier is considered to be username here + $entity = new IdentityObject(); + $entity->setUsername('a username'); + $entity->setPassword('a password'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository->expects($this->exactly(1)) + ->method('find') + ->with($this->equalTo('a username')) + ->will($this->returnValue($entity)); + + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $metadata->expects($this->exactly(1)) + ->method('getIdentifierValues') + ->with($this->equalTo($entity)) + ->will($this->returnValue($entity->getUsername())); + + $storage = new ObjectRepositoryStorage(array( + 'objectRepository' => $objectRepository, + 'classMetadata' => $metadata, + 'storage' => new NonPersistentStorage() + )); + + $storage->write($entity); + $this->assertFalse($storage->isEmpty()); + + $result = $storage->read(); + $this->assertEquals($entity, $result); + + $key = $storage->readKeyOnly(); + $this->assertEquals('a username', $key); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/DoctrineCacheStorageTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/DoctrineCacheStorageTest.php new file mode 100644 index 00000000..ac8ecad1 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/DoctrineCacheStorageTest.php @@ -0,0 +1,729 @@ +. + */ + +namespace DoctrineModuleTest\Cache; + +use DoctrineModule\Cache\DoctrineCacheStorage; +use Doctrine\Common\Cache\ArrayCache; + +use Zend\Cache\Storage\Adapter\AdapterOptions; + +use PHPUnit_Framework_TestCase; +use Zend\Stdlib\ErrorHandler; + +/** + * Tests for the cache bridge + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + * @todo extend \ZendTest\Cache\Storage\CommonAdapterTest instead + */ +class DoctrineCacheStorageTest extends PHPUnit_Framework_TestCase +{ + /** + * @var AdapterOptions + */ + protected $_options; + + /** + * The storage adapter + * + * @var \Zend\Cache\Storage\StorageInterface + */ + protected $_storage; + + /** + * All datatypes of PHP + * + * @var string[] + */ + protected $_phpDatatypes = array( + 'NULL', 'boolean', 'integer', 'double', + 'string', 'array', 'object', 'resource' + ); + + public function setUp() + { + $this->_options = new AdapterOptions(); + // @todo fix constructor as it is messy + $this->_storage = new DoctrineCacheStorage($this->_options, new ArrayCache()); + + $this->assertInstanceOf( + 'Zend\Cache\Storage\StorageInterface', + $this->_storage, + 'Storage adapter instance is needed for tests' + ); + $this->assertInstanceOf( + 'Zend\Cache\Storage\Adapter\AdapterOptions', + $this->_options, + 'Options instance is needed for tests' + ); + } + + public function tearDown() + { + // be sure the error handler has been stopped + if (ErrorHandler::started()) { + ErrorHandler::stop(); + $this->fail('ErrorHandler not stopped'); + } + } + + public function testOptionNamesValid() + { + $options = $this->_storage->getOptions()->toArray(); + foreach ($options as $name => $value) { + $this->assertRegExp( + '/^[a-z]+[a-z0-9_]*[a-z0-9]+$/', + $name, + "Invalid option name '{$name}'" + ); + } + } + + public function testGettersAndSettersOfOptionsExists() + { + $options = $this->_storage->getOptions(); + foreach ($options->toArray() as $option => $value) { + if ($option == 'adapter') { + // Skip this, as it's a "special" value + continue; + } + $method = ucwords(str_replace('_', ' ', $option)); + $method = str_replace(' ', '', $method); + + $this->assertTrue( + method_exists($options, 'set' . $method), + "Missing method 'set'{$method}" + ); + + $this->assertTrue( + method_exists($options, 'get' . $method), + "Missing method 'get'{$method}" + ); + } + } + + public function testOptionsGetAndSetDefault() + { + $options = $this->_storage->getOptions(); + $this->_storage->setOptions($options); + $this->assertSame($options, $this->_storage->getOptions()); + } + + public function testOptionsFluentInterface() + { + $options = $this->_storage->getOptions(); + foreach ($options->toArray() as $option => $value) { + $method = ucwords(str_replace('_', ' ', $option)); + $method = 'set' . str_replace(' ', '', $method); + $this->assertSame( + $options, + $options->{$method}($value), + "Method '{$method}' doesn't implement the fluent interface" + ); + } + + $this->assertSame( + $this->_storage, + $this->_storage->setOptions($options), + "Method 'setOptions' doesn't implement the fluent interface" + ); + } + + public function testGetCapabilities() + { + $capabilities = $this->_storage->getCapabilities(); + $this->assertInstanceOf('Zend\Cache\Storage\Capabilities', $capabilities); + } + + public function testDatatypesCapability() + { + $capabilities = $this->_storage->getCapabilities(); + $datatypes = $capabilities->getSupportedDatatypes(); + $this->assertInternalType('array', $datatypes); + + foreach ($datatypes as $sourceType => $targetType) { + $this->assertContains( + $sourceType, $this->_phpDatatypes, + "Unknown source type '{$sourceType}'" + ); + if (is_string($targetType)) { + $this->assertContains( + $targetType, $this->_phpDatatypes, + "Unknown target type '{$targetType}'" + ); + } else { + $this->assertInternalType( + 'bool', $targetType, + "Target type must be a string or boolean" + ); + } + } + } + + public function testSupportedMetadataCapability() + { + $capabilities = $this->_storage->getCapabilities(); + $metadata = $capabilities->getSupportedMetadata(); + $this->assertInternalType('array', $metadata); + + foreach ($metadata as $property) { + $this->assertInternalType('string', $property); + } + } + + public function testTtlCapabilities() + { + $capabilities = $this->_storage->getCapabilities(); + + $this->assertInternalType('integer', $capabilities->getMaxTtl()); + $this->assertGreaterThanOrEqual(0, $capabilities->getMaxTtl()); + + $this->assertInternalType('bool', $capabilities->getStaticTtl()); + + $this->assertInternalType('numeric', $capabilities->getTtlPrecision()); + $this->assertGreaterThan(0, $capabilities->getTtlPrecision()); + + $this->assertInternalType('bool', $capabilities->getExpiredRead()); + } + + public function testKeyCapabilities() + { + $capabilities = $this->_storage->getCapabilities(); + + $this->assertInternalType('integer', $capabilities->getMaxKeyLength()); + $this->assertGreaterThanOrEqual(-1, $capabilities->getMaxKeyLength()); + + $this->assertInternalType('bool', $capabilities->getNamespaceIsPrefix()); + + $this->assertInternalType('string', $capabilities->getNamespaceSeparator()); + } + + public function testHasItemReturnsTrueOnValidItem() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertTrue($this->_storage->hasItem('key')); + } + + public function testHasItemReturnsFalseOnMissingItem() + { + $this->assertFalse($this->_storage->hasItem('key')); + } + + public function testHasItemNonReadable() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + + $this->_options->setReadable(false); + $this->assertFalse($this->_storage->hasItem('key')); + } + + public function testHasItemsReturnsKeysOfFoundItems() + { + $this->assertTrue($this->_storage->setItem('key1', 'value1')); + $this->assertTrue($this->_storage->setItem('key2', 'value2')); + + $result = $this->_storage->hasItems(array('missing', 'key1', 'key2')); + sort($result); + + $expectedResult = array('key1', 'key2'); + $this->assertEquals($expectedResult, $result); + } + + public function testHasItemsReturnsEmptyArrayIfNonReadable() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + + $this->_options->setReadable(false); + $this->assertEquals(array(), $this->_storage->hasItems(array('key'))); + } + + public function testGetItemReturnsNullOnMissingItem() + { + $this->assertNull($this->_storage->getItem('unknwon')); + } + + public function testGetItemSetsSuccessFlag() + { + $success = null; + + // $success = false on get missing item + $this->_storage->getItem('unknown', $success); + $this->assertFalse($success); + + // $success = true on get valid item + $this->_storage->setItem('test', 'test'); + $this->_storage->getItem('test', $success); + $this->assertTrue($success); + } + + public function testGetItemReturnsNullIfNonReadable() + { + $this->_options->setReadable(false); + + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertNull($this->_storage->getItem('key')); + } + + public function testGetItemsReturnsKeyValuePairsOfFoundItems() + { + $this->assertTrue($this->_storage->setItem('key1', 'value1')); + $this->assertTrue($this->_storage->setItem('key2', 'value2')); + + $result = $this->_storage->getItems(array('missing', 'key1', 'key2')); + ksort($result); + + $expectedResult = array( + 'key1' => 'value1', + 'key2' => 'value2', + ); + $this->assertEquals($expectedResult, $result); + } + + public function testGetItemsReturnsEmptyArrayIfNonReadable() + { + $this->_options->setReadable(false); + + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertEquals(array(), $this->_storage->getItems(array('key'))); + } + + public function testGetMetadata() + { + $capabilities = $this->_storage->getCapabilities(); + $supportedMetadatas = $capabilities->getSupportedMetadata(); + + $this->assertTrue($this->_storage->setItem('key', 'value')); + $metadata = $this->_storage->getMetadata('key'); + + $this->assertInternalType('array', $metadata); + foreach ($supportedMetadatas as $supportedMetadata) { + $this->assertArrayHasKey($supportedMetadata, $metadata); + } + } + + public function testGetMetadataReturnsFalseOnMissingItem() + { + $this->assertFalse($this->_storage->getMetadata('unknown')); + } + + public function testGetMetadataReturnsFalseIfNonReadable() + { + $this->_options->setReadable(false); + + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertFalse($this->_storage->getMetadata('key')); + } + + public function testGetMetadatas() + { + $capabilities = $this->_storage->getCapabilities(); + $supportedMetadatas = $capabilities->getSupportedMetadata(); + + $items = array( + 'key1' => 'value1', + 'key2' => 'value2' + ); + $this->assertSame(array(), $this->_storage->setItems($items)); + + $metadatas = $this->_storage->getMetadatas(array_keys($items)); + $this->assertInternalType('array', $metadatas); + $this->assertSame(count($items), count($metadatas)); + foreach ($metadatas as $k => $metadata) { + $this->assertInternalType('array', $metadata); + foreach ($supportedMetadatas as $supportedMetadata) { + $this->assertArrayHasKey($supportedMetadata, $metadata); + } + } + } + + public function testGetMetadatasReturnsEmptyArrayIfNonReadable() + { + $this->_options->setReadable(false); + + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertEquals(array(), $this->_storage->getMetadatas(array('key'))); + } + + public function testSetGetHasAndRemoveItem() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertEquals('value', $this->_storage->getItem('key')); + $this->assertTrue($this->_storage->hasItem('key')); + + $this->assertTrue($this->_storage->removeItem('key')); + $this->assertFalse($this->_storage->hasItem('key')); + $this->assertNull($this->_storage->getItem('key')); + } + + public function testSetGetHasAndRemoveItems() + { + $items = array( + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ); + + $this->assertSame(array(), $this->_storage->setItems($items)); + + $rs = $this->_storage->getItems(array_keys($items)); + $this->assertInternalType('array', $rs); + foreach ($items as $key => $value) { + $this->assertArrayHasKey($key, $rs); + $this->assertEquals($value, $rs[$key]); + } + + $rs = $this->_storage->hasItems(array_keys($items)); + $this->assertInternalType('array', $rs); + $this->assertEquals(count($items), count($rs)); + foreach ($items as $key => $value) { + $this->assertContains($key, $rs); + } + + $this->assertSame(array('missing'), $this->_storage->removeItems(array('missing', 'key1', 'key3'))); + unset($items['key1'], $items['key3']); + + $rs = $this->_storage->getItems(array_keys($items)); + $this->assertInternalType('array', $rs); + foreach ($items as $key => $value) { + $this->assertArrayHasKey($key, $rs); + $this->assertEquals($value, $rs[$key]); + } + + $rs = $this->_storage->hasItems(array_keys($items)); + $this->assertInternalType('array', $rs); + $this->assertEquals(count($items), count($rs)); + foreach ($items as $key => $value) { + $this->assertContains($key, $rs); + } + } + + public function testSetGetHasAndRemoveItemWithNamespace() + { + // write "key" to default namespace + $this->_options->setNamespace('defaultns1'); + $this->assertTrue( $this->_storage->setItem('key', 'defaultns1') ); + + // write "key" to an other default namespace + $this->_options->setNamespace('defaultns2'); + $this->assertTrue( $this->_storage->setItem('key', 'defaultns2') ); + + // test value of defaultns2 + $this->assertTrue($this->_storage->hasItem('key')); + $this->assertEquals('defaultns2', $this->_storage->getItem('key') ); + + // test value of defaultns1 + $this->_options->setNamespace('defaultns1'); + $this->assertTrue($this->_storage->hasItem('key')); + $this->assertEquals('defaultns1', $this->_storage->getItem('key') ); + + // remove item of defaultns1 + $this->_options->setNamespace('defaultns1'); + $this->assertTrue($this->_storage->removeItem('key')); + $this->assertFalse($this->_storage->hasItem('key')); + + // remove item of defaultns2 + $this->_options->setNamespace('defaultns2'); + $this->assertTrue($this->_storage->removeItem('key')); + $this->assertFalse($this->_storage->hasItem('key')); + } + + public function testSetGetHasAndRemoveItemsWithNamespace() + { + $items = array( + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ); + + $this->_options->setNamespace('defaultns1'); + $this->assertSame(array(), $this->_storage->setItems($items)); + + $this->_options->setNamespace('defaultns2'); + $this->assertSame(array(), $this->_storage->hasItems(array_keys($items))); + + $this->_options->setNamespace('defaultns1'); + $rs = $this->_storage->getItems(array_keys($items)); + $this->assertInternalType('array', $rs); + foreach ($items as $key => $value) { + $this->assertArrayHasKey($key, $rs); + $this->assertEquals($value, $rs[$key]); + } + + $rs = $this->_storage->hasItems(array_keys($items)); + $this->assertInternalType('array', $rs); + $this->assertEquals(count($items), count($rs)); + foreach ($items as $key => $value) { + $this->assertContains($key, $rs); + } + + // remove the first and the last item + $this->assertSame(array('missing'), $this->_storage->removeItems(array('missing', 'key1', 'key3'))); + unset($items['key1'], $items['key3']); + + $rs = $this->_storage->getItems(array_keys($items)); + $this->assertInternalType('array', $rs); + foreach ($items as $key => $value) { + $this->assertArrayHasKey($key, $rs); + $this->assertEquals($value, $rs[$key]); + } + + $rs = $this->_storage->hasItems(array_keys($items)); + $this->assertInternalType('array', $rs); + $this->assertEquals(count($items), count($rs)); + foreach ($items as $key => $value) { + $this->assertContains($key, $rs); + } + } + + public function testSetAndGetItemOfDifferentTypes() + { + $capabilities = $this->_storage->getCapabilities(); + + $types = array( + 'NULL' => null, + 'boolean' => true, + 'integer' => 12345, + 'double' => 123.45, + 'string' => 'string', // already tested + 'array' => array('one', 'tow' => 'two', 'three' => array('four' => 'four')), + 'object' => new \stdClass(), + 'resource' => fopen(__FILE__, 'r'), + ); + $types['object']->one = 'one'; + $types['object']->two = new \stdClass(); + $types['object']->two->three = 'three'; + + foreach ($capabilities->getSupportedDatatypes() as $sourceType => $targetType) { + if ($targetType === false) { + continue; + } + + $value = $types[$sourceType]; + $this->assertTrue($this->_storage->setItem('key', $value), "Failed to set type '{$sourceType}'"); + + if ($targetType === true) { + $this->assertSame($value, $this->_storage->getItem('key')); + } elseif (is_string($targetType)) { + settype($value, $targetType); + $this->assertEquals($value, $this->_storage->getItem('key')); + } + } + } + + public function testSetItemReturnsFalseIfNonWritable() + { + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->setItem('key', 'value')); + $this->assertFalse($this->_storage->hasItem('key')); + } + + public function testAddNewItem() + { + $this->assertTrue($this->_storage->addItem('key', 'value')); + $this->assertTrue($this->_storage->hasItem('key')); + } + + public function testAddItemReturnsFalseIfItemAlreadyExists() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertFalse($this->_storage->addItem('key', 'newValue')); + } + + public function testAddItemReturnsFalseIfNonWritable() + { + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->addItem('key', 'value')); + $this->assertFalse($this->_storage->hasItem('key')); + } + + public function testAddItemsReturnsFailedKeys() + { + $this->assertTrue($this->_storage->setItem('key1', 'value1')); + + $failedKeys = $this->_storage->addItems(array( + 'key1' => 'XYZ', + 'key2' => 'value2', + )); + + $this->assertSame(array('key1'), $failedKeys); + $this->assertSame('value1', $this->_storage->getItem('key1')); + $this->assertTrue($this->_storage->hasItem('key2')); + } + + public function testReplaceExistingItem() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + $this->assertTrue($this->_storage->replaceItem('key', 'anOtherValue')); + $this->assertEquals('anOtherValue', $this->_storage->getItem('key')); + } + + public function testReplaceItemReturnsFalseOnMissingItem() + { + $this->assertFalse($this->_storage->replaceItem('missingKey', 'value')); + } + + public function testReplaceItemReturnsFalseIfNonWritable() + { + $this->_storage->setItem('key', 'value'); + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->replaceItem('key', 'newvalue')); + $this->assertEquals('value', $this->_storage->getItem('key')); + } + + public function testReplaceItemsReturnsFailedKeys() + { + $this->assertTrue($this->_storage->setItem('key1', 'value1')); + + $failedKeys = $this->_storage->replaceItems(array( + 'key1' => 'XYZ', + 'key2' => 'value2', + )); + + $this->assertSame(array('key2'), $failedKeys); + $this->assertSame('XYZ', $this->_storage->getItem('key1')); + $this->assertFalse($this->_storage->hasItem('key2')); + } + + public function testRemoveItemReturnsFalseOnMissingItem() + { + $this->assertFalse($this->_storage->removeItem('missing')); + } + + public function testRemoveItemsReturnsMissingKeys() + { + $this->_storage->setItem('key', 'value'); + $this->assertSame(array('missing'), $this->_storage->removeItems(array('key', 'missing'))); + } + + public function testCheckAndSetItem() + { + $this->assertTrue($this->_storage->setItem('key', 'value')); + + $success = null; + $casToken = null; + $this->assertEquals('value', $this->_storage->getItem('key', $success, $casToken)); + $this->assertNotNull($casToken); + + $this->assertTrue($this->_storage->checkAndSetItem($casToken, 'key', 'newValue')); + $this->assertFalse($this->_storage->checkAndSetItem($casToken, 'key', 'failedValue')); + $this->assertEquals('newValue', $this->_storage->getItem('key')); + } + + public function testIncrementItem() + { + $this->assertTrue($this->_storage->setItem('counter', 10)); + $this->assertEquals(15, $this->_storage->incrementItem('counter', 5)); + $this->assertEquals(15, $this->_storage->getItem('counter')); + } + + public function testIncrementItemInitialValue() + { + $this->assertEquals(5, $this->_storage->incrementItem('counter', 5)); + $this->assertEquals(5, $this->_storage->getItem('counter')); + } + + public function testIncrementItemReturnsFalseIfNonWritable() + { + $this->_storage->setItem('key', 10); + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->incrementItem('key', 5)); + $this->assertEquals(10, $this->_storage->getItem('key')); + } + + public function testIncrementItemsResturnsKeyValuePairsOfWrittenItems() + { + $this->assertTrue($this->_storage->setItem('key1', 10)); + + $result = $this->_storage->incrementItems(array( + 'key1' => 10, + 'key2' => 10, + )); + ksort($result); + + $this->assertSame(array( + 'key1' => 20, + 'key2' => 10, + ), $result); + } + + public function testIncrementItemsReturnsEmptyArrayIfNonWritable() + { + $this->_storage->setItem('key', 10); + $this->_options->setWritable(false); + + $this->assertSame(array(), $this->_storage->incrementItems(array('key' => 5))); + $this->assertEquals(10, $this->_storage->getItem('key')); + } + + public function testDecrementItem() + { + $this->assertTrue($this->_storage->setItem('counter', 30)); + $this->assertEquals(25, $this->_storage->decrementItem('counter', 5)); + $this->assertEquals(25, $this->_storage->getItem('counter')); + } + + public function testDecrementItemInitialValue() + { + $this->assertEquals(-5, $this->_storage->decrementItem('counter', 5)); + $this->assertEquals(-5, $this->_storage->getItem('counter')); + } + + public function testDecrementItemReturnsFalseIfNonWritable() + { + $this->_storage->setItem('key', 10); + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->decrementItem('key', 5)); + $this->assertEquals(10, $this->_storage->getItem('key')); + } + + public function testDecrementItemsReturnsEmptyArrayIfNonWritable() + { + $this->_storage->setItem('key', 10); + $this->_options->setWritable(false); + + $this->assertSame(array(), $this->_storage->decrementItems(array('key' => 5))); + $this->assertEquals(10, $this->_storage->getItem('key')); + } + + public function testTouchItemReturnsFalseOnMissingItem() + { + $this->assertFalse($this->_storage->touchItem('missing')); + } + + public function testTouchItemReturnsFalseIfNonWritable() + { + $this->_options->setWritable(false); + + $this->assertFalse($this->_storage->touchItem('key')); + } + + public function testTouchItemsReturnsGivenKeysIfNonWritable() + { + $this->_options->setWritable(false); + $this->assertSame(array('key'), $this->_storage->touchItems(array('key'))); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/ZendStorageCacheTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/ZendStorageCacheTest.php new file mode 100644 index 00000000..c536e4f3 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Cache/ZendStorageCacheTest.php @@ -0,0 +1,113 @@ +. + */ + +namespace DoctrineModuleTest\Cache; + +use DoctrineModule\Cache\ZendStorageCache; +use Doctrine\Common\Cache\Cache; +use Zend\Cache\Storage\Adapter\Memory; +use PHPUnit_Framework_TestCase; + +/** + * Tests for the cache bridge + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ZendStorageCacheTest extends PHPUnit_Framework_TestCase +{ + /** + * @return ZendStorageCache + */ + protected function getCacheDriver() + { + return new ZendStorageCache(new Memory()); + } + + public function testBasics() + { + $cache = $this->getCacheDriver(); + + // Test save + $cache->save('test_key', 'testing this out'); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key')); + + // Test fetch + $this->assertEquals('testing this out', $cache->fetch('test_key')); + + // Test delete + $cache->save('test_key2', 'test2'); + $cache->delete('test_key2'); + $this->assertFalse($cache->contains('test_key2')); + + // Fetch/save test with objects (Is cache driver serializes/unserializes objects correctly ?) + $cache->save('test_object_key', new \ArrayObject()); + $this->assertTrue($cache->fetch('test_object_key') instanceof \ArrayObject); + } + + public function testDeleteAll() + { + $cache = $this->getCacheDriver(); + $cache->save('test_key1', '1'); + $cache->save('test_key2', '2'); + $cache->deleteAll(); + + $this->assertFalse($cache->contains('test_key1')); + $this->assertFalse($cache->contains('test_key2')); + } + + public function testFlushAll() + { + $cache = $this->getCacheDriver(); + $cache->save('test_key1', '1'); + $cache->save('test_key2', '2'); + $cache->flushAll(); + + $this->assertFalse($cache->contains('test_key1')); + $this->assertFalse($cache->contains('test_key2')); + } + + public function testNamespace() + { + $cache = $this->getCacheDriver(); + $cache->setNamespace('test_'); + $cache->save('key1', 'test'); + + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('test2_'); + + $this->assertFalse($cache->contains('key1')); + } + + public function testGetStats() + { + $cache = $this->getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertArrayHasKey(Cache::STATS_HITS, $stats); + $this->assertArrayHasKey(Cache::STATS_MISSES, $stats); + $this->assertArrayHasKey(Cache::STATS_UPTIME, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILIABLE, $stats); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/ProxyTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/ProxyTest.php new file mode 100644 index 00000000..98d3789b --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/ProxyTest.php @@ -0,0 +1,372 @@ +. + */ + +namespace DoctrineModuleTest\Form\Element; + +use Doctrine\Common\Collections\ArrayCollection; +use DoctrineModule\Form\Element\Proxy; +use DoctrineModuleTest\Form\Element\TestAsset\FormObject; +use PHPUnit_Framework_TestCase; + +/** + * Tests for the Collection pagination adapter + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class ProxyTest extends PHPUnit_Framework_TestCase +{ + protected $metadata; + protected $proxy; + + /** + * {@inheritDoc}. + */ + protected function setUp() + { + parent::setUp(); + $this->proxy = new Proxy; + + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage No object manager was set + */ + public function testExceptionThrownForMissingObjectManager() + { + $this->proxy->setOptions(array( + 'target_class' => 'DoctrineModuleTest\Form\Element\TestAsset\FormObject' + )); + $this->proxy->getValueOptions(); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage No target class was set + */ + public function testExceptionThrownForMissingTargetClass() + { + $this->proxy->setOptions(array( + 'object_manager' => $this->getMock('Doctrine\Common\Persistence\ObjectManager'), + )); + $this->proxy->getValueOptions(); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage No method name was set + */ + public function testExceptionThrownForMissingFindMethodName() + { + $objectClass = 'DoctrineModuleTest\Form\Element\TestAsset\FormObject'; + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($metadata)); + + $this->proxy->setOptions(array( + 'object_manager' => $objectManager, + 'target_class' => $objectClass, + 'find_method' => array('no_name') + )); + + $this->proxy->getValueOptions(); + } + + public function testExceptionFindMethodNameNotExistentInRepository() + { + $objectClass = 'DoctrineModuleTest\Form\Element\TestAsset\FormObject'; + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($metadata)); + + $objectManager->expects($this->once()) + ->method('getRepository') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($objectRepository)); + + $this->proxy->setOptions(array( + 'object_manager' => $objectManager, + 'target_class' => $objectClass, + 'find_method' => array('name' => 'NotExistent'), + )); + + $this->setExpectedException('RuntimeException', 'Method "NotExistent" could not be found in respository "' . get_class($objectRepository).'"'); + + $this->proxy->getValueOptions(); + } + + public function testToStringIsUsedForGetValueOptions() + { + $this->prepareProxy(); + + $result = $this->proxy->getValueOptions(); + $this->assertEquals($result[0]['label'], 'object one username'); + $this->assertEquals($result[1]['label'], 'object two username'); + $this->assertEquals($result[0]['value'], 1); + $this->assertEquals($result[1]['value'], 2); + } + + public function testPropertyGetterUsedForGetValueOptions() + { + $this->prepareProxy(); + + $this->proxy->setOptions(array('property' => 'password')); + + $this->metadata->expects($this->exactly(2)) + ->method('hasField') + ->with($this->equalTo('password')) + ->will($this->returnValue(true)); + + $result = $this->proxy->getValueOptions(); + $this->assertEquals($result[0]['label'], 'object one password'); + $this->assertEquals($result[1]['label'], 'object two password'); + $this->assertEquals($result[0]['value'], 1); + $this->assertEquals($result[1]['value'], 2); + } + + public function testPublicPropertyUsedForGetValueOptions() + { + $this->prepareProxy(); + + $this->proxy->setOptions(array('property' => 'email')); + + $this->metadata->expects($this->exactly(2)) + ->method('hasField') + ->with($this->equalTo('email')) + ->will($this->returnValue(true)); + + $result = $this->proxy->getValueOptions(); + $this->assertEquals($result[0]['label'], 'object one email'); + $this->assertEquals($result[1]['label'], 'object two email'); + $this->assertEquals($result[0]['value'], 1); + $this->assertEquals($result[1]['value'], 2); + } + + public function testIsMethodOptionUsedForGetValueOptions() + { + $this->prepareProxy(); + + $this->proxy->setOptions(array('property' => 'name', 'is_method' => true)); + + $this->metadata->expects($this->never()) + ->method('hasField'); + + $result = $this->proxy->getValueOptions(); + $this->assertEquals($result[0]['label'], 'object one firstname object one surname'); + $this->assertEquals($result[1]['label'], 'object two firstname object two surname'); + $this->assertEquals($result[0]['value'], 1); + $this->assertEquals($result[1]['value'], 2); + } + + public function testCanWorkWithEmptyTables() + { + $this->prepareEmptyProxy(); + + $result = $this->proxy->getValueOptions(); + $this->assertEquals(array(), $result); + } + + public function testUsingFindMethod() + { + $this->prepareFilteredProxy(); + + $this->proxy->getValueOptions(); + } + + protected function prepareProxy() + { + $objectClass = 'DoctrineModuleTest\Form\Element\TestAsset\FormObject'; + $objectOne = new FormObject; + $objectTwo = new FormObject; + + $objectOne->setId(1) + ->setUsername('object one username') + ->setPassword('object one password') + ->setEmail('object one email') + ->setFirstname('object one firstname') + ->setSurname('object one surname'); + + $objectTwo->setId(2) + ->setUsername('object two username') + ->setPassword('object two password') + ->setEmail('object two email') + ->setFirstname('object two firstname') + ->setSurname('object two surname'); + + $result = new ArrayCollection(array( + $objectOne, + $objectTwo + )); + + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $metadata->expects($this->exactly(2)) + ->method('getIdentifierValues') + ->will($this->returnCallback( + function() use ($objectOne, $objectTwo) { + $input = func_get_args(); + $input = array_shift($input); + if ($input == $objectOne) { + return array('id' => 1); + } else if ($input == $objectTwo) { + return array('id' => 2); + } else { + return array(); + } + } + )); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository->expects($this->once()) + ->method('findAll') + ->will($this->returnValue($result)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($metadata)); + + $objectManager->expects($this->once()) + ->method('getRepository') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($objectRepository)); + + $this->proxy->setOptions(array( + 'object_manager' => $objectManager, + 'target_class' => $objectClass + )); + + $this->metadata = $metadata; + } + + protected function prepareFilteredProxy() + { + $objectClass = 'DoctrineModuleTest\Form\Element\TestAsset\FormObject'; + $objectOne = new FormObject; + $objectTwo = new FormObject; + + $objectOne->setId(1) + ->setUsername('object one username') + ->setPassword('object one password') + ->setEmail('object one email') + ->setFirstname('object one firstname') + ->setSurname('object one surname'); + + $objectTwo->setId(2) + ->setUsername('object two username') + ->setPassword('object two password') + ->setEmail('object two email') + ->setFirstname('object two firstname') + ->setSurname('object two surname'); + + $result = new ArrayCollection(array( + $objectOne, + $objectTwo + )); + + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $metadata->expects($this->exactly(2)) + ->method('getIdentifierValues') + ->will($this->returnCallback( + function() use ($objectOne, $objectTwo) { + $input = func_get_args(); + $input = array_shift($input); + if ($input == $objectOne) { + return array('id' => 1); + } else if ($input == $objectTwo) { + return array('id' => 2); + } else { + return array(); + } + } + )); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository->expects($this->once()) + ->method('findBy') + ->will($this->returnValue($result)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($metadata)); + + $objectManager->expects($this->once()) + ->method('getRepository') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($objectRepository)); + + $this->proxy->setOptions(array( + 'object_manager' => $objectManager, + 'target_class' => $objectClass, + 'find_method' => array('name' => 'findBy', + 'params' => array( + 'criteria' => array('email' => 'object one email'), + ), + ), + )); + + $this->metadata = $metadata; + } + + public function prepareEmptyProxy() + { + $objectClass = 'DoctrineModuleTest\Form\Element\TestAsset\FormObject'; + + $result = new ArrayCollection(); + + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + + $objectRepository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $objectRepository->expects($this->once()) + ->method('findAll') + ->will($this->returnValue($result)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->once()) + ->method('getClassMetadata') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($metadata)); + + $objectManager->expects($this->once()) + ->method('getRepository') + ->with($this->equalTo($objectClass)) + ->will($this->returnValue($objectRepository)); + + $this->proxy->setOptions(array( + 'object_manager' => $objectManager, + 'target_class' => $objectClass + )); + + $this->metadata = $metadata; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/TestAsset/FormObject.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/TestAsset/FormObject.php new file mode 100644 index 00000000..bb55d565 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Form/Element/TestAsset/FormObject.php @@ -0,0 +1,167 @@ +. + */ + +namespace DoctrineModuleTest\Form\Element\TestAsset; + +/** + * Simple mock object for form element adapter tests + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class FormObject +{ + /** + * @var int|null + */ + protected $id; + + /** + * @var string|null + */ + public $email; + + /** + * @var string|null + */ + protected $username; + + /** + * @var string|null + */ + protected $firstname; + + /** + * @var string|null + */ + protected $surname; + + /** + * @var string|null + */ + protected $password; + + public function __toString() + { + return $this->username; + } + + /** + * @param int $id + */ + public function setId($id) + { + $this->id = (int) $id; + return $this; + } + + /** + * @param string $email + */ + public function setEmail($email) + { + $this->email = (string) $email; + return $this; + } + + /** + * @return string|null + */ + public function getEmail() + { + return $this->email; + } + + /** + * @param string $password + */ + public function setPassword($password) + { + $this->password = (string) $password; + return $this; + } + + /** + * @return string|null + */ + public function getPassword() + { + return $this->password; + } + + /** + * @param string $username + */ + public function setUsername($username) + { + $this->username = (string) $username; + return $this; + } + + /** + * @return string|null + */ + public function getUsername() + { + return $this->username; + } + + /** + * @param string $firstname + */ + public function setFirstname($firstname) + { + $this->firstname = (string) $firstname; + return $this; + } + + /** + * @return string|null + */ + public function getFirstname() + { + return $this->firstname; + } + + /** + * @param string $surname + */ + public function setSurname($surname) + { + $this->surname = (string) $surname; + return $this; + } + + /** + * @return string|null + */ + public function getSurname() + { + return $this->surname; + } + + /** + * @return string|null + */ + public function getName() + { + return isset($this->firstname) && isset($this->surname)? $this->firstname . " " . $this->surname : null; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Paginator/Adapter/CollectionAdapterTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Paginator/Adapter/CollectionAdapterTest.php new file mode 100644 index 00000000..27392992 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Paginator/Adapter/CollectionAdapterTest.php @@ -0,0 +1,74 @@ +. + */ + +namespace DoctrineModuleTest\Paginator\Adapter; + +use DoctrineModule\Paginator\Adapter\Collection as CollectionAdapter; +use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit_Framework_TestCase; + +/** + * Tests for the Collection pagination adapter + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class CollectionAdapterTest extends PHPUnit_Framework_TestCase +{ + /** + * @var CollectionAdapter + */ + protected $adapter; + + /** + * {@inheritDoc}. + */ + protected function setUp () + { + parent::setUp(); + $this->adapter = new CollectionAdapter(new ArrayCollection(range(1, 101))); + } + + public function testGetsItemsAtOffsetZero() + { + $expected = range(1, 10); + $actual = $this->adapter->getItems(0, 10); + $this->assertEquals($expected, $actual); + } + + public function testGetsItemsAtOffsetTen() + { + $expected = range(11, 20); + $actual = $this->adapter->getItems(10, 10); + $this->assertEquals($expected, $actual); + } + + public function testReturnsCorrectCount() + { + $this->assertEquals(101, $this->adapter->count()); + } + + public function testEmptySet() + { + $adapter = new CollectionAdapter(new ArrayCollection()); + $actual = $adapter->getItems(0, 10); + $this->assertEquals(array(), $actual); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AdapterFactoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AdapterFactoryTest.php new file mode 100644 index 00000000..30489a84 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AdapterFactoryTest.php @@ -0,0 +1,56 @@ +. + */ + +namespace DoctrineModuleTest\Service\Authentication; + +use DoctrineModule\Service\Authentication\AdapterFactory; +use PHPUnit_Framework_TestCase as BaseTestCase; +use Zend\ServiceManager\ServiceManager; + +class AdapterFactoryTest extends BaseTestCase +{ + public function testWillInstantiateFromFQCN() + { + + $name = 'testFactory'; + $factory = new AdapterFactory($name); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'authentication' => array( + $name => array( + 'objectManager' => $objectManager, + 'identityClass' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + 'identityProperty' => 'username', + 'credentialProperty' => 'password' + ), + ), + ), + ) + ); + + $adapter = $factory->createService($serviceManager); + $this->assertInstanceOf('DoctrineModule\Authentication\Adapter\ObjectRepository', $adapter); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AuthenticationServiceFactoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AuthenticationServiceFactoryTest.php new file mode 100644 index 00000000..02b2dc89 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/AuthenticationServiceFactoryTest.php @@ -0,0 +1,60 @@ +. + */ + +namespace DoctrineModuleTest\Service\Authentication; + +use DoctrineModule\Service\Authentication\AuthenticationServiceFactory; +use DoctrineModule\Service\Authentication\AdapterFactory; +use DoctrineModule\Service\Authentication\StorageFactory; +use PHPUnit_Framework_TestCase as BaseTestCase; +use Zend\ServiceManager\ServiceManager; + +class AuthenticationServiceFactoryTest extends BaseTestCase +{ + public function testWillInstantiateFromFQCN() + { + + $name = 'testFactory'; + $factory = new AuthenticationServiceFactory($name); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'authentication' => array( + $name => array( + 'objectManager' => $objectManager, + 'identityClass' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + 'identityProperty' => 'username', + 'credentialProperty' => 'password' + ), + ), + ), + ) + ); + $serviceManager->setFactory('doctrine.authenticationadapter.' . $name, new AdapterFactory($name)); + $serviceManager->setFactory('doctrine.authenticationstorage.' . $name, new StorageFactory($name)); + + $authenticationService = $factory->createService($serviceManager); + $this->assertInstanceOf('Zend\Authentication\AuthenticationService', $authenticationService); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/StorageFactoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/StorageFactoryTest.php new file mode 100644 index 00000000..5a51dfad --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/Authentication/StorageFactoryTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace DoctrineModuleTest\Service\Authentication; + +use DoctrineModule\Service\Authentication\StorageFactory; +use PHPUnit_Framework_TestCase as BaseTestCase; +use Zend\ServiceManager\ServiceManager; + +class StorageFactoryTest extends BaseTestCase +{ + public function testWillInstantiateFromFQCN() + { + $name = 'testFactory'; + $factory = new StorageFactory($name); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'authentication' => array( + $name => array( + 'objectManager' => $objectManager, + 'identityClass' => 'DoctrineModuleTest\Authentication\Adapter\TestAsset\IdentityObject', + 'identityProperty' => 'username', + 'credentialProperty' => 'password' + ), + ), + ), + ) + ); + + $adapter = $factory->createService($serviceManager); + $this->assertInstanceOf('DoctrineModule\Authentication\Storage\ObjectRepository', $adapter); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/EventManagerFactoryTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/EventManagerFactoryTest.php new file mode 100644 index 00000000..f0d9a747 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/EventManagerFactoryTest.php @@ -0,0 +1,146 @@ +. + */ + +namespace DoctrineModuleTest\Service; + +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Service\EventManagerFactory; +use Zend\ServiceManager\ServiceManager; +use DoctrineModuleTest\Service\TestAsset\DummyEventSubscriber; + +/** + * Base test case to be used when a service manager instance is required + */ +class EventManagerFactoryTest extends BaseTestCase +{ + public function testWillInstantiateFromFQCN() + { + $name = 'eventManagerFactory'; + $factory = new EventManagerFactory($name); + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'eventmanager' => array( + $name => array( + 'subscribers' => array( + __NAMESPACE__ . '\TestAsset\DummyEventSubscriber' + ), + ), + ), + ), + ) + ); + + /* $var $eventManager \Doctrine\Common\EventManager */ + $eventManager = $factory->createService($serviceManager); + $this->assertInstanceOf('Doctrine\Common\EventManager', $eventManager); + + $listeners = $eventManager->getListeners('dummy'); + $this->assertCount(1, $listeners); + } + + public function testWillAttachEventListenersFromConfiguredInstances() + { + $name = 'eventManagerFactory'; + $factory = new EventManagerFactory($name); + $subscriber = new DummyEventSubscriber(); + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'eventmanager' => array( + $name => array( + 'subscribers' => array( + $subscriber, + ), + ), + ), + ), + ) + ); + + /* $var $eventManager \Doctrine\Common\EventManager */ + $eventManager = $factory->createService($serviceManager); + $this->assertInstanceOf('Doctrine\Common\EventManager', $eventManager); + + $listeners = $eventManager->getListeners(); + $this->assertArrayHasKey('dummy', $listeners); + $listeners = $eventManager->getListeners('dummy'); + $this->assertContains($subscriber, $listeners); + } + + public function testWillAttachEventListenersFromServiceManagerAlias() + { + $name = 'eventManagerFactory'; + $factory = new EventManagerFactory($name); + $subscriber = new DummyEventSubscriber(); + $serviceManager = new ServiceManager(); + $serviceManager->setService('dummy-subscriber', $subscriber); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'eventmanager' => array( + $name => array( + 'subscribers' => array( + 'dummy-subscriber' + ), + ), + ), + ), + ) + ); + + /* $var $eventManager \Doctrine\Common\EventManager */ + $eventManager = $factory->createService($serviceManager); + $this->assertInstanceOf('Doctrine\Common\EventManager', $eventManager); + + $listeners = $eventManager->getListeners(); + $this->assertArrayHasKey('dummy', $listeners); + $listeners = $eventManager->getListeners('dummy'); + $this->assertContains($subscriber, $listeners); + } + + public function testWillRefuseNonExistingSubscriber() + { + $name = 'eventManagerFactory'; + $factory = new EventManagerFactory($name); + $serviceManager = new ServiceManager(); + $serviceManager->setService( + 'Configuration', + array( + 'doctrine' => array( + 'eventmanager' => array( + $name => array( + 'subscribers' => array( + 'non-existing-subscriber' + ), + ), + ), + ), + ) + ); + + $this->setExpectedException('InvalidArgumentException'); + $factory->createService($serviceManager); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/TestAsset/DummyEventSubscriber.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/TestAsset/DummyEventSubscriber.php new file mode 100644 index 00000000..90296e5a --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Service/TestAsset/DummyEventSubscriber.php @@ -0,0 +1,45 @@ +. + */ + +namespace DoctrineModuleTest\Service\TestAsset; + +use Doctrine\Common\EventSubscriber; + +/** + * Dummy event subscriber used to test injections + */ +class DummyEventSubscriber implements EventSubscriber +{ + /** + * Empty callback method + */ + public function dummy() + { + } + + /** + * {@inheritDoc} + */ + public function getSubscribedEvents() + { + return array( + 'dummy' + ); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/ServiceManagerTestCase.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/ServiceManagerTestCase.php new file mode 100644 index 00000000..31c0b737 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/ServiceManagerTestCase.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineModuleTest; + +use PHPUnit_Framework_TestCase as BaseTestCase; +use Zend\ServiceManager\ServiceManager; +use Zend\Mvc\Service\ServiceManagerConfig; + +/** + * Base test case to be used when a service manager instance is required + */ +class ServiceManagerTestCase extends BaseTestCase +{ + /** + * @var array + */ + private static $configuration = array(); + + /** + * @static + * @param array $configuration + */ + public static function setServiceManagerConfiguration(array $configuration) + { + static::$configuration = $configuration; + } + + /** + * @static + * @return array + */ + public static function getServiceManagerConfiguration() + { + return static::$configuration; + } + + /** + * Retrieves a new ServiceManager instance + * + * @param array|null $configuration + * @return ServiceManager + */ + public function getServiceManager(array $configuration = null) + { + $configuration = $configuration ?: static::getServiceManagerConfiguration(); + $serviceManager = new ServiceManager(new ServiceManagerConfig($configuration)); + $serviceManager->setService('ApplicationConfiguration', $configuration); + $serviceManager->setFactory('ServiceListener', 'Zend\Mvc\Service\ServiceListenerFactory'); + /* @var $moduleManager \Zend\ModuleManager\ModuleManagerInterface */ + $moduleManager = $serviceManager->get('ModuleManager'); + $moduleManager->loadModules(); + + return $serviceManager; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToManyEntity.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToManyEntity.php new file mode 100644 index 00000000..42a8520d --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToManyEntity.php @@ -0,0 +1,66 @@ +entities = new ArrayCollection(); + } + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } + + public function addEntities(Collection $entities, $modifyValue = true) + { + foreach ($entities as $entity) { + // Modify the value to illustrate the difference between by value and by reference + if ($modifyValue) { + $entity->setField('Modified from addEntities adder', false); + } + + $this->entities->add($entity); + } + } + + public function removeEntities(Collection $entities) + { + foreach ($entities as $entity) { + $this->entities->removeElement($entity); + } + } + + public function getEntities($modifyValue = true) + { + // Modify the value to illustrate the difference between by value and by reference + if ($modifyValue) { + foreach ($this->entities as $entity) { + $entity->setField('Modified from getEntities getter', false); + } + } + + return $this->entities; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToOneEntity.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToOneEntity.php new file mode 100644 index 00000000..c7c0ba02 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/OneToOneEntity.php @@ -0,0 +1,49 @@ +id = $id; + } + + public function getId() + { + return $this->id; + } + + public function setToOne(SimpleEntity $entity = null, $modifyValue = true) + { + // Modify the value to illustrate the difference between by value and by reference + if ($modifyValue && $entity !== null) { + $entity->setField('Modified from setToOne setter', false); + } + + $this->toOne = $entity; + } + + public function getToOne($modifyValue = true) + { + // Make some modifications to the association so that we can demonstrate difference between + // by value and by reference + if ($modifyValue) { + $this->toOne->setField('Modified from getToOne getter', false); + } + + return $this->toOne; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntity.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntity.php new file mode 100644 index 00000000..7adf6fd5 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntity.php @@ -0,0 +1,47 @@ +id = $id; + } + + public function getId() + { + return $this->id; + } + + public function setField($field, $modifyValue = true) + { + // Modify the value to illustrate the difference between by value and by reference + if ($modifyValue) { + $this->field = "From setter: $field"; + } else { + $this->field = $field; + } + } + + public function getField($modifyValue = true) + { + // Modify the value to illustrate the difference between by value and by reference + if ($modifyValue) { + return "From getter: $this->field"; + } else { + return $this->field; + } + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntityWithDateTime.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntityWithDateTime.php new file mode 100644 index 00000000..dc9144cc --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/Asset/SimpleEntityWithDateTime.php @@ -0,0 +1,38 @@ +id = $id; + } + + public function getId() + { + return $this->id; + } + + public function setDate(DateTime $date) + { + $this->date = $date; + } + + public function getDate() + { + return $this->date; + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTest.php new file mode 100644 index 00000000..1e118085 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTest.php @@ -0,0 +1,1190 @@ +metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $this->objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $this->objectManager->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($this->metadata)); + } + + public function configureObjectManagerForSimpleEntity() + { + $refl = new ReflectionClass('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity'); + + $this->metadata->expects($this->any()) + ->method('getName') + ->will($this->returnValue('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity')); + $this->metadata->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array())); + + $this->metadata->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('id', 'field'))); + + $this->metadata->expects($this->any()) + ->method('getTypeOfField') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('field'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return 'integer'; + } elseif ($arg === 'field') { + return 'string'; + } + })); + + $this->metadata->expects($this->any()) + ->method('hasAssociation') + ->will($this->returnValue(false)); + + $this->metadata->expects($this->any()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + + $this->metadata->expects($this->any()) + ->method('getReflectionClass') + ->will($this->returnValue($refl)); + + $this->hydratorByValue = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', true); + $this->hydratorByReference = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', false); + } + + public function configureObjectManagerForSimpleEntityWithDateTime() + { + $refl = new ReflectionClass('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntityWithDateTime'); + + $this->metadata->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array())); + + $this->metadata->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('id', 'date'))); + + $this->metadata->expects($this->any()) + ->method('getTypeOfField') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('date'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return 'integer'; + } elseif ($arg === 'date') { + return 'datetime'; + } + })); + + $this->metadata->expects($this->any()) + ->method('hasAssociation') + ->will($this->returnValue(false)); + + $this->metadata->expects($this->any()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + + $this->metadata->expects($this->any()) + ->method('getReflectionClass') + ->will($this->returnValue($refl)); + + $this->hydratorByValue = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntityWithDateTime', true); + $this->hydratorByReference = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntityWithDateTime', false); + } + + public function configureObjectManagerForOneToOneEntity() + { + $refl = new ReflectionClass('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity'); + + $this->metadata->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('id'))); + + $this->metadata->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array('toOne'))); + + $this->metadata->expects($this->any()) + ->method('getTypeOfField') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('toOne'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return 'integer'; + } elseif ($arg === 'toOne') { + return 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity'; + } + })); + + $this->metadata->expects($this->any()) + ->method('hasAssociation') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('toOne'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return false; + } elseif ($arg === 'toOne') { + return true; + } + })); + + $this->metadata->expects($this->any()) + ->method('isSingleValuedAssociation') + ->with('toOne') + ->will($this->returnValue(true)); + + $this->metadata->expects($this->any()) + ->method('getAssociationTargetClass') + ->with('toOne') + ->will($this->returnValue('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity')); + + $this->metadata->expects($this->any()) + ->method('getReflectionClass') + ->will($this->returnValue($refl)); + + $this->hydratorByValue = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', true); + $this->hydratorByReference = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', false); + } + + public function configureObjectManagerForOneToManyEntity() + { + $refl = new ReflectionClass('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity'); + + $this->metadata->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('id'))); + + $this->metadata->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array('entities'))); + + $this->metadata->expects($this->any()) + ->method('getTypeOfField') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('entities'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return 'integer'; + } elseif ($arg === 'entities') { + return 'Doctrine\Common\Collections\ArrayCollection'; + } + })); + + $this->metadata->expects($this->any()) + ->method('hasAssociation') + ->with($this->logicalOr( + $this->equalTo('id'), + $this->equalTo('entities'))) + ->will($this->returnCallback(function($arg) { + if ($arg === 'id') { + return false; + } elseif ($arg === 'entities') { + return true; + } + })); + + $this->metadata->expects($this->any()) + ->method('isSingleValuedAssociation') + ->with('entities') + ->will($this->returnValue(false)); + + $this->metadata->expects($this->any()) + ->method('isCollectionValuedAssociation') + ->with('entities') + ->will($this->returnValue(true)); + + $this->metadata->expects($this->any()) + ->method('getAssociationTargetClass') + ->with('entities') + ->will($this->returnValue('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity')); + + $this->metadata->expects($this->any()) + ->method('getReflectionClass') + ->will($this->returnValue($refl)); + + $this->hydratorByValue = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', true); + $this->hydratorByReference = new DoctrineObjectHydrator($this->objectManager, 'DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', false); + } + + public function testCanExtractSimpleEntityByValue() + { + // When using extraction by value, it will use the public API of the entity to retrieve values (getters) + $entity = new Asset\SimpleEntity(); + $entity->setId(2); + $entity->setField('foo', false); + + $this->configureObjectManagerForSimpleEntity(); + + $data = $this->hydratorByValue->extract($entity); + $this->assertEquals(array('id' => 2, 'field' => 'From getter: foo'), $data); + } + + public function testCanExtractSimpleEntityByReference() + { + // When using extraction by reference, it won't use the public API of entity (getters won't be called) + $entity = new Asset\SimpleEntity(); + $entity->setId(2); + $entity->setField('foo', false); + + $this->configureObjectManagerForSimpleEntity(); + + $data = $this->hydratorByReference->extract($entity); + $this->assertEquals(array('id' => 2, 'field' => 'foo'), $data); + } + + public function testCanHydrateSimpleEntityByValue() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\SimpleEntity(); + $this->configureObjectManagerForSimpleEntity(); + $data = array('field' => 'foo'); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity); + $this->assertEquals('From setter: foo', $entity->getField(false)); + } + + public function testCanHydrateSimpleEntityByReference() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\SimpleEntity(); + $this->configureObjectManagerForSimpleEntity(); + $data = array('field' => 'foo'); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity); + $this->assertEquals('foo', $entity->getField(false)); + } + + public function testReuseExistingEntityIfDataArrayContainsIdentifier() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\SimpleEntity(); + + $this->configureObjectManagerForSimpleEntity(); + $data = array('id' => 1); + + $entityInDatabaseWithIdOfOne = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfOne->setId(1); + $entityInDatabaseWithIdOfOne->setField('bar', false); + + $this + ->objectManager + ->expects($this->once()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', array('id' => 1)) + ->will($this->returnValue($entityInDatabaseWithIdOfOne)); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity); + $this->assertEquals('bar', $entity->getField(false)); + } + + public function testExtractOneToOneAssociationByValue() + { + // When using extraction by value, it will use the public API of the entity to retrieve values (getters) + $toOne = new Asset\SimpleEntity(); + $toOne->setId(2); + $toOne->setField('foo', false); + + $entity = new Asset\OneToOneEntity(); + $entity->setId(2); + $entity->setToOne($toOne); + + $this->configureObjectManagerForOneToOneEntity(); + + $data = $this->hydratorByValue->extract($entity); + + $this->assertEquals(2, $data['id']); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $data['toOne']); + $this->assertEquals('Modified from getToOne getter', $data['toOne']->getField(false)); + $this->assertSame($toOne, $data['toOne']); + } + + public function testExtractOneToOneAssociationByReference() + { + // When using extraction by value, it will use the public API of the entity to retrieve values (getters) + $toOne = new Asset\SimpleEntity(); + $toOne->setId(2); + $toOne->setField('foo', false); + + $entity = new Asset\OneToOneEntity(); + $entity->setId(2); + $entity->setToOne($toOne, false); + + $this->configureObjectManagerForOneToOneEntity(); + + $data = $this->hydratorByReference->extract($entity); + + $this->assertEquals(2, $data['id']); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $data['toOne']); + $this->assertEquals('foo', $data['toOne']->getField(false)); + $this->assertSame($toOne, $data['toOne']); + } + + public function testHydrateOneToOneAssociationByValue() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $toOne = new Asset\SimpleEntity(); + $toOne->setId(2); + $toOne->setField('foo', false); + + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + $data = array('toOne' => $toOne); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertEquals('Modified from setToOne setter', $entity->getToOne(false)->getField(false)); + } + + public function testHydrateOneToOneAssociationByReference() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $toOne = new Asset\SimpleEntity(); + $toOne->setId(2); + $toOne->setField('foo', false); + + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + $data = array('toOne' => $toOne); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertEquals('foo', $entity->getToOne(false)->getField(false)); + } + + public function testHydrateOneToOneAssociationByValueUsingIdentifierForRelation() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + // Use entity of id 1 as relation + $data = array('toOne' => 1); + + $entityInDatabaseWithIdOfOne = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfOne->setId(1); + $entityInDatabaseWithIdOfOne->setField('bar', false); + + $this + ->objectManager + ->expects($this->once()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', 1) + ->will($this->returnValue($entityInDatabaseWithIdOfOne)); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertSame($entityInDatabaseWithIdOfOne, $entity->getToOne(false)); + } + + public function testHydrateOneToOneAssociationByReferenceUsingIdentifierForRelation() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + // Use entity of id 1 as relation + $data = array('toOne' => 1); + + $entityInDatabaseWithIdOfOne = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfOne->setId(1); + $entityInDatabaseWithIdOfOne->setField('bar', false); + + $this + ->objectManager + ->expects($this->once()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', 1) + ->will($this->returnValue($entityInDatabaseWithIdOfOne)); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertSame($entityInDatabaseWithIdOfOne, $entity->getToOne(false)); + } + + public function testHydrateOneToOneAssociationByValueUsingIdentifierArrayForRelation() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + // Use entity of id 1 as relation + $data = array('toOne' => array('id' => 1)); + + $entityInDatabaseWithIdOfOne = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfOne->setId(1); + $entityInDatabaseWithIdOfOne->setField('bar', false); + + $this + ->objectManager + ->expects($this->once()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', array('id' => 1)) + ->will($this->returnValue($entityInDatabaseWithIdOfOne)); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertSame($entityInDatabaseWithIdOfOne, $entity->getToOne(false)); + } + + public function testHydrateOneToOneAssociationByReferenceUsingIdentifierArrayForRelation() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + // Use entity of id 1 as relation + $data = array('toOne' => array('id' => 1)); + + $entityInDatabaseWithIdOfOne = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfOne->setId(1); + $entityInDatabaseWithIdOfOne->setField('bar', false); + + $this + ->objectManager + ->expects($this->once()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', array('id' => 1)) + ->will($this->returnValue($entityInDatabaseWithIdOfOne)); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity', $entity); + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity->getToOne(false)); + $this->assertSame($entityInDatabaseWithIdOfOne, $entity->getToOne(false)); + } + + public function testCanHydrateOneToOneAssociationByValueWithNullableRelation() + { + // When using hydration by value, it will use the public API of the entity to retrieve values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + $data = array('toOne' => null); + + $this->metadata->expects($this->once()) + ->method('hasAssociation'); + + $object = $this->hydratorByValue->hydrate($data, $entity); + $this->assertNull($object->getToOne(false)); + } + + public function testCanHydrateOneToOneAssociationByReferenceWithNullableRelation() + { + // When using hydration by reference, it won't use the public API of the entity to retrieve values (setters) + $entity = new Asset\OneToOneEntity(); + $this->configureObjectManagerForOneToOneEntity(); + + $data = array('toOne' => null); + + $this->metadata->expects($this->once()) + ->method('hasAssociation'); + + $object = $this->hydratorByReference->hydrate($data, $entity); + $this->assertNull($object->getToOne(false)); + } + + public function testExtractOneToManyAssociationByValue() + { + // When using extraction by value, it will use the public API of the entity to retrieve values (getters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $collection = new ArrayCollection(array($toMany1, $toMany2)); + + $entity = new Asset\OneToManyEntity(); + $entity->setId(4); + $entity->addEntities($collection); + + $this->configureObjectManagerForOneToManyEntity(); + + $data = $this->hydratorByValue->extract($entity); + + $this->assertEquals(4, $data['id']); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $data['entities']); + + $this->assertEquals($toMany1->getId(), $data['entities'][0]->getId()); + $this->assertSame($toMany1, $data['entities'][0]); + $this->assertEquals($toMany2->getId(), $data['entities'][1]->getId()); + $this->assertSame($toMany2, $data['entities'][1]); + } + + public function testExtractOneToManyAssociationByReference() + { + // When using extraction by reference, it won't use the public API of the entity to retrieve values (getters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $collection = new ArrayCollection(array($toMany1, $toMany2)); + + $entity = new Asset\OneToManyEntity(); + $entity->setId(4); + $entity->addEntities($collection); + + $this->configureObjectManagerForOneToManyEntity(); + + $data = $this->hydratorByReference->extract($entity); + + $this->assertEquals(4, $data['id']); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $data['entities']); + + $this->assertEquals($toMany1->getId(), $data['entities'][0]->getId()); + $this->assertSame($toMany1, $data['entities'][0]); + $this->assertEquals($toMany2->getId(), $data['entities'][1]->getId()); + $this->assertSame($toMany2, $data['entities'][1]); + } + + public function testHydrateOneToManyAssociationByValue() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + $toMany1, $toMany2 + ) + ); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($toMany1, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($toMany2, $entities[1]); + } + + public function testHydrateOneToManyAssociationByReference() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + $toMany1, $toMany2 + ) + ); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertNotContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($toMany1, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($toMany2, $entities[1]); + } + + public function testHydrateOneToManyAssociationByValueUsingIdentifiersForRelations() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + 2, 3 + ) + ); + + $entityInDatabaseWithIdOfTwo = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfTwo->setId(2); + $entityInDatabaseWithIdOfTwo->setField('foo', false); + + $entityInDatabaseWithIdOfThree = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfThree->setId(3); + $entityInDatabaseWithIdOfThree->setField('bar', false); + + $this + ->objectManager + ->expects($this->exactly(2)) + ->method('find') + ->with( + 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', + $this->logicalOr( + $this->equalTo(2), + $this->equalTo(3) + ) + ) + ->will($this->returnCallback( + function($target, $arg) use ($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree) { + if ($arg === 2) { + return $entityInDatabaseWithIdOfTwo; + } elseif ($arg === 3) { + return $entityInDatabaseWithIdOfThree; + } + } + )); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + /* @var $entity \DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity */ + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($entityInDatabaseWithIdOfTwo, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($entityInDatabaseWithIdOfThree, $entities[1]); + } + + public function testHydrateOneToManyAssociationByValueUsingIdentifiersArrayForRelations() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + array('id' => 2), + array('id' => 3) + ) + ); + + $entityInDatabaseWithIdOfTwo = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfTwo->setId(2); + $entityInDatabaseWithIdOfTwo->setField('foo', false); + + $entityInDatabaseWithIdOfThree = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfThree->setId(3); + $entityInDatabaseWithIdOfThree->setField('bar', false); + + $this + ->objectManager + ->expects($this->exactly(2)) + ->method('find') + ->with( + 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', + $this->logicalOr( + $this->equalTo(array('id' => 2)), + $this->equalTo(array('id' => 3)) + ) + ) + ->will($this->returnCallback( + function($target, $arg) use ($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree) { + if ($arg['id'] === 2) { + return $entityInDatabaseWithIdOfTwo; + } elseif ($arg['id'] === 3) { + return $entityInDatabaseWithIdOfThree; + } + } + )); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + /* @var $entity \DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity */ + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($entityInDatabaseWithIdOfTwo, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($entityInDatabaseWithIdOfThree, $entities[1]); + } + + public function testHydrateOneToManyAssociationByReferenceUsingIdentifiersArrayForRelations() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + array('id' => 2), + array('id' => 3) + ) + ); + + $entityInDatabaseWithIdOfTwo = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfTwo->setId(2); + $entityInDatabaseWithIdOfTwo->setField('foo', false); + + $entityInDatabaseWithIdOfThree = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfThree->setId(3); + $entityInDatabaseWithIdOfThree->setField('bar', false); + + $this + ->objectManager + ->expects($this->exactly(2)) + ->method('find') + ->with( + 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', + $this->logicalOr( + $this->equalTo(array('id' => 2)), + $this->equalTo(array('id' => 3)) + ) + ) + ->will($this->returnCallback( + function($target, $arg) use ($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree) { + if ($arg['id'] === 2) { + return $entityInDatabaseWithIdOfTwo; + } elseif ($arg['id'] === 3) { + return $entityInDatabaseWithIdOfThree; + } + } + )); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertNotContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($entityInDatabaseWithIdOfTwo, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($entityInDatabaseWithIdOfThree, $entities[1]); + } + + public function testHydrateOneToManyAssociationByReferenceUsingIdentifiersForRelations() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + 2, 3 + ) + ); + + $entityInDatabaseWithIdOfTwo = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfTwo->setId(2); + $entityInDatabaseWithIdOfTwo->setField('foo', false); + + $entityInDatabaseWithIdOfThree = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfThree->setId(3); + $entityInDatabaseWithIdOfThree->setField('bar', false); + + $this + ->objectManager + ->expects($this->any()) + ->method('find') + ->with( + 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', + $this->logicalOr( + $this->equalTo(2), + $this->equalTo(3) + ) + ) + ->will($this->returnCallback( + function($target, $arg) use ($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree) { + if ($arg === 2) { + return $entityInDatabaseWithIdOfTwo; + } elseif ($arg === 3) { + return $entityInDatabaseWithIdOfThree; + } + } + )); + + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + $entities = $entity->getEntities(false); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + $this->assertNotContains('Modified from addEntities adder', $en->getField(false)); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($entityInDatabaseWithIdOfTwo, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($entityInDatabaseWithIdOfThree, $entities[1]); + } + + public function testHydrateOneToManyAssociationByValueUsingDisallowRemoveStrategy() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $toMany3 = new Asset\SimpleEntity(); + $toMany3->setId(8); + $toMany3->setField('baz', false); + + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + // Initally add two elements + $entity->addEntities(new ArrayCollection(array($toMany1, $toMany2))); + + // The hydrated collection contains two other elements, one of them is new, and one of them is missing + // in the new strategy + $data = array( + 'entities' => array( + $toMany2, $toMany3 + ) + ); + + // Use a DisallowRemove strategy + $this->hydratorByValue->addStrategy('entities', new Strategy\DisallowRemoveByValue()); + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $entities = $entity->getEntities(false); + + // DisallowStrategy should not remove existing entities in Collection even if it's not in the new collection + $this->assertEquals(3, count($entities)); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($toMany1, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($toMany2, $entities[1]); + + $this->assertEquals(8, $entities[2]->getId()); + $this->assertSame($toMany3, $entities[2]); + } + + public function testHydrateOneToManyAssociationByReferenceUsingDisallowRemoveStrategy() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $toMany3 = new Asset\SimpleEntity(); + $toMany3->setId(8); + $toMany3->setField('baz', false); + + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + // Initally add two elements + $entity->addEntities(new ArrayCollection(array($toMany1, $toMany2))); + + // The hydrated collection contains two other elements, one of them is new, and one of them is missing + // in the new strategy + $data = array( + 'entities' => array( + $toMany2, $toMany3 + ) + ); + + // Use a DisallowRemove strategy + $this->hydratorByReference->addStrategy('entities', new Strategy\DisallowRemoveByReference()); + $entity = $this->hydratorByReference->hydrate($data, $entity); + + $entities = $entity->getEntities(false); + + // DisallowStrategy should not remove existing entities in Collection even if it's not in the new collection + $this->assertEquals(3, count($entities)); + + foreach ($entities as $en) { + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $en); + $this->assertInternalType('integer', $en->getId()); + + // Only the third element is new so the adder has not been called on it + if ($en === $toMany3) { + $this->assertNotContains('Modified from addEntities adder', $en->getField(false)); + } + } + + $this->assertEquals(2, $entities[0]->getId()); + $this->assertSame($toMany1, $entities[0]); + + $this->assertEquals(3, $entities[1]->getId()); + $this->assertSame($toMany2, $entities[1]); + + $this->assertEquals(8, $entities[2]->getId()); + $this->assertSame($toMany3, $entities[2]); + } + + public function testAssertCollectionsAreNotSwappedDuringHydration() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $toMany1 = new Asset\SimpleEntity(); + $toMany1->setId(2); + $toMany1->setField('foo', false); + + $toMany2 = new Asset\SimpleEntity(); + $toMany2->setId(3); + $toMany2->setField('bar', false); + + $data = array( + 'entities' => array( + $toMany1, $toMany2 + ) + ); + + // Set the initial collection + $entity->addEntities(new ArrayCollection(array($toMany1, $toMany2))); + $initialCollection = $entity->getEntities(false); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $modifiedCollection = $entity->getEntities(false); + $this->assertSame($initialCollection, $modifiedCollection); + } + + public function testAssertCollectionsAreNotSwappedDuringHydrationUsingIdentifiersForRelations() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + 2, 3 + ) + ); + + $entityInDatabaseWithIdOfTwo = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfTwo->setId(2); + $entityInDatabaseWithIdOfTwo->setField('foo', false); + + $entityInDatabaseWithIdOfThree = new Asset\SimpleEntity(); + $entityInDatabaseWithIdOfThree->setId(3); + $entityInDatabaseWithIdOfThree->setField('bar', false); + + // Set the initial collection + $entity->addEntities(new ArrayCollection(array($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree))); + $initialCollection = $entity->getEntities(false); + + $this + ->objectManager + ->expects($this->any()) + ->method('find') + ->with( + 'DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', + $this->logicalOr( + $this->equalTo(2), + $this->equalTo(3) + ) + ) + ->will($this->returnCallback( + function($arg) use ($entityInDatabaseWithIdOfTwo, $entityInDatabaseWithIdOfThree) { + if ($arg === 2) { + return $entityInDatabaseWithIdOfTwo; + } elseif ($arg === 3) { + return $entityInDatabaseWithIdOfThree; + } + } + )); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $modifiedCollection = $entity->getEntities(false); + $this->assertSame($initialCollection, $modifiedCollection); + } + + public function testCanLookupsForEmptyIdentifiers() + { + // When using hydration by reference, it won't use the public API of the entity to set values (setters) + $entity = new Asset\OneToManyEntity(); + $this->configureObjectManagerForOneToManyEntity(); + + $data = array( + 'entities' => array( + '' + ) + ); + $entityInDatabaseWithEmptyId = new Asset\SimpleEntity(); + $entityInDatabaseWithEmptyId->setId(''); + $entityInDatabaseWithEmptyId->setField('baz', false); + + $this + ->objectManager + ->expects($this->any()) + ->method('find') + ->with('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', '') + ->will($this->returnValue($entityInDatabaseWithEmptyId)); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToManyEntity', $entity); + + $entities = $entity->getEntities(false); + $entity = $entities[0]; + + $this->assertEquals(1, count($entities)); + + $this->assertInstanceOf('DoctrineModuleTest\Stdlib\Hydrator\Asset\SimpleEntity', $entity); + $this->assertSame($entityInDatabaseWithEmptyId, $entity); + } + + public function testHandleDateTimeConversionUsingByValue() + { + // When using hydration by value, it will use the public API of the entity to set values (setters) + $entity = new Asset\SimpleEntityWithDateTime(); + $this->configureObjectManagerForSimpleEntityWithDateTime(); + + $now = time(); + $data = array('date' => $now); + + $entity = $this->hydratorByValue->hydrate($data, $entity); + + $this->assertInstanceOf('DateTime', $entity->getDate()); + $this->assertEquals($now, $entity->getDate()->getTimestamp()); + } + + public function testAssertStrategiesForCollectionsAreAlwaysAddedWhenHydratorIsConstructed() + { + $this->configureObjectManagerForOneToManyEntity(); + + $this->assertTrue($this->hydratorByValue->hasStrategy('entities')); + $this->assertTrue($this->hydratorByReference->hasStrategy('entities')); + + $this->assertFalse($this->hydratorByValue->hasStrategy('id')); + $this->assertFalse($this->hydratorByReference->hasStrategy('id')); + } + + public function testAssertDefaultStrategyForCollectionsIsAllowRemove() + { + $this->configureObjectManagerForOneToManyEntity(); + + $this->assertInstanceOf('DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByValue', $this->hydratorByValue->getStrategy('entities')); + $this->assertEquals('entities', $this->hydratorByValue->getStrategy('entities')->getCollectionName()); + + $this->assertInstanceOf('DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByReference', $this->hydratorByReference->getStrategy('entities')); + $this->assertEquals('entities', $this->hydratorByReference->getStrategy('entities')->getCollectionName()); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/NoObjectExistsTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/NoObjectExistsTest.php new file mode 100644 index 00000000..88ac6f4d --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/NoObjectExistsTest.php @@ -0,0 +1,64 @@ +. + */ + +namespace DoctrineModuleTest\Validator\Adapter; + +use stdClass; +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Validator\NoObjectExists; + +/** + * Tests for the NoObjectExists tests + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class NoObjectExistsTest extends BaseTestCase +{ + public function testCanValidateWithNoAvailableObjectInRepository() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->will($this->returnValue(null)); + + $validator = new NoObjectExists(array( + 'object_repository' => $repository, + 'fields' => 'matchKey', + )); + $this->assertTrue($validator->isValid('matchValue')); + } + + public function testCannotValidateWithAvailableObjectInRepository() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->will($this->returnValue(new stdClass())); + + $validator = new NoObjectExists(array( + 'object_repository' => $repository, + 'fields' => 'matchKey', + )); + $this->assertFalse($validator->isValid('matchValue')); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/ObjectExistsTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/ObjectExistsTest.php new file mode 100644 index 00000000..d81dc615 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/ObjectExistsTest.php @@ -0,0 +1,174 @@ +. + */ + +namespace DoctrineModuleTest\Validator\Adapter; + +use stdClass; +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Validator\ObjectExists; + +/** + * Tests for the ObjectExists validator + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ObjectExistsTest extends BaseTestCase +{ + public function testCanValidateWithSingleField() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->exactly(2)) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue(new stdClass())); + + $validator = new ObjectExists(array( + 'object_repository' => $repository, + 'fields' => 'matchKey', + )); + $this->assertTrue($validator->isValid('matchValue')); + $this->assertTrue($validator->isValid(array('matchKey' => 'matchValue'))); + } + + public function testCanValidateWithMultipleFields() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->exactly(2)) + ->method('findOneBy') + ->with(array( + 'firstMatchKey' => 'firstMatchValue', + 'secondMatchKey' => 'secondMatchValue', + )) + ->will($this->returnValue(new stdClass())); + + $validator = new ObjectExists(array( + 'object_repository' => $repository, + 'fields' => array( + 'firstMatchKey', + 'secondMatchKey', + ), + )); + $this->assertTrue($validator->isValid(array( + 'firstMatchKey' => 'firstMatchValue', + 'secondMatchKey' => 'secondMatchValue', + ))); + $this->assertTrue($validator->isValid(array('firstMatchValue', 'secondMatchValue'))); + } + + public function testCanValidateFalseOnNoResult() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->will($this->returnValue(null)); + + $validator = new ObjectExists(array( + 'object_repository' => $repository, + 'fields' => 'field', + )); + $this->assertFalse($validator->isValid('value')); + } + + public function testWillRefuseMissingRepository() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'fields' => 'field', + )); + } + + public function testWillRefuseNonObjectRepository() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'object_repository' => 'invalid', + 'fields' => 'field', + )); + } + + public function testWillRefuseInvalidRepository() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'object_repository' => new stdClass(), + 'fields' => 'field', + )); + } + + public function testWillRefuseMissingFields() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'object_repository' => $this->getMock('Doctrine\Common\Persistence\ObjectRepository'), + )); + } + + public function testWillRefuseEmptyFields() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'object_repository' => $this->getMock('Doctrine\Common\Persistence\ObjectRepository'), + 'fields' => array(), + )); + } + + public function testWillRefuseNonStringFields() + { + $this->setExpectedException('Zend\Validator\Exception\InvalidArgumentException'); + new ObjectExists(array( + 'object_repository' => $this->getMock('Doctrine\Common\Persistence\ObjectRepository'), + 'fields' => array( + 123 + ), + )); + } + + public function testWillNotValidateOnFieldsCountMismatch() + { + $this->setExpectedException( + 'Zend\Validator\Exception\RuntimeException', + 'Provided values count is 1, while expected number of fields to be matched is 2' + ); + $validator = new ObjectExists(array( + 'object_repository' => $this->getMock('Doctrine\Common\Persistence\ObjectRepository'), + 'fields' => array('field1', 'field2') + )); + $validator->isValid(array('field1Value')); + } + + public function testWillNotValidateOnFieldKeysMismatch() + { + $this->setExpectedException( + 'Zend\Validator\Exception\RuntimeException', + 'Field "field2" was not provided, but was expected since the configured field lists needs it for validation' + ); + $validator = new ObjectExists(array( + 'object_repository' => $this->getMock('Doctrine\Common\Persistence\ObjectRepository'), + 'fields' => array('field1', 'field2') + )); + $validator->isValid(array( + 'field1' => 'field1Value', + )); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/UniqueObjectTest.php b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/UniqueObjectTest.php new file mode 100644 index 00000000..d182329c --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/DoctrineModuleTest/Validator/UniqueObjectTest.php @@ -0,0 +1,244 @@ +. + */ + +namespace DoctrineModuleTest\Validator\Adapter; + +use stdClass; +use PHPUnit_Framework_TestCase as BaseTestCase; +use DoctrineModule\Validator\UniqueObject; + +/** + * Tests for the UniqueObject validator + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Oskar Bley + */ +class UniqueObjectTest extends BaseTestCase +{ + public function testCanValidateWithNotAvailableObjectInRepository() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue(null)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $validator = new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey', + ) + ); + $this->assertTrue($validator->isValid('matchValue')); + } + + public function testCanValidateIfThereIsTheSameObjectInTheRepository() + { + $match = new stdClass(); + + $classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $classMetadata + ->expects($this->once()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + $classMetadata + ->expects($this->once()) + ->method('getIdentifierValues') + ->with($match) + ->will($this->returnValue(array('id' => 'identifier'))); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->any()) + ->method('getClassMetadata') + ->with('stdClass') + ->will($this->returnValue($classMetadata)); + + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue('stdClass')); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue($match)); + + $validator = new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey' + ) + ); + $this->assertTrue($validator->isValid('matchValue', array('id' => 'identifier'))); + } + + public function testCannotValidateIfThereIsAnotherObjectWithTheSameValueInTheRepository() + { + $match = new stdClass(); + + $classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $classMetadata + ->expects($this->once()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + $classMetadata + ->expects($this->once()) + ->method('getIdentifierValues') + ->with($match) + ->will($this->returnValue(array('id' => 'identifier'))); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->any()) + ->method('getClassMetadata') + ->with('stdClass') + ->will($this->returnValue($classMetadata)); + + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue('stdClass')); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue($match)); + + $validator = new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey' + ) + ); + $this->assertFalse($validator->isValid('matchValue', array('id' => 'another identifier'))); + } + + /** + * @expectedException Zend\Validator\Exception\RuntimeException + * @expectedExceptionMessage Expected context to be an array but is null + */ + public function testThrowsAnExceptionOnMissingContext() + { + $match = new stdClass(); + + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue($match)); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + $validator = new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey', + ) + ); + $validator->isValid('matchValue'); + } + + /** + * @expectedException Zend\Validator\Exception\RuntimeException + * @expectedExceptionMessage Expected context to contain id + */ + public function testThrowsAnExceptionOnMissingIdentifierInContext() + { + $match = new stdClass(); + + $classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $classMetadata + ->expects($this->once()) + ->method('getIdentifierFieldNames') + ->will($this->returnValue(array('id'))); + + $objectManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $objectManager->expects($this->any()) + ->method('getClassMetadata') + ->with('stdClass') + ->will($this->returnValue($classMetadata)); + + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + $repository + ->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue('stdClass')); + $repository + ->expects($this->once()) + ->method('findOneBy') + ->with(array('matchKey' => 'matchValue')) + ->will($this->returnValue($match)); + + $validator = new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey' + ) + ); + $validator->isValid('matchValue', array()); + } + + /** + * @expectedException Zend\Validator\Exception\InvalidArgumentException + * @expectedExceptionMessage Option "object_manager" is required and must be + * an instance of Doctrine\Common\Persistence\ObjectManager, nothing given + */ + public function testThrowsAnExceptionOnMissingObjectManager() + { + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + + new UniqueObject( + array( + 'object_repository' => $repository, + 'fields' => 'matchKey' + ) + ); + } + + /** + * @expectedException Zend\Validator\Exception\InvalidArgumentException + * @expectedExceptionMessage Option "object_manager" is required and must be + * an instance of Doctrine\Common\Persistence\ObjectManager, nothing given + */ + public function testThrowsAnExceptionOnWrongObjectManager() + { + $objectManager = new stdClass(); + + $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); + + new UniqueObject( + array( + 'object_repository' => $repository, + 'object_manager' => $objectManager, + 'fields' => 'matchKey' + ) + ); + } +} diff --git a/vendor/doctrine/doctrine-module/tests/TestConfiguration.php.dist b/vendor/doctrine/doctrine-module/tests/TestConfiguration.php.dist new file mode 100644 index 00000000..6a7f6cc6 --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/TestConfiguration.php.dist @@ -0,0 +1,15 @@ + array( + 'DoctrineModule', + ), + 'module_listener_options' => array( + 'config_glob_paths' => array( + 'config/autoload/{,*.}{global,local}.php', + ), + 'module_paths' => array( + './module', + './vendor', + ), + ), +); \ No newline at end of file diff --git a/vendor/doctrine/doctrine-module/tests/phpunit.xml b/vendor/doctrine/doctrine-module/tests/phpunit.xml new file mode 100644 index 00000000..3d1f1f2b --- /dev/null +++ b/vendor/doctrine/doctrine-module/tests/phpunit.xml @@ -0,0 +1,11 @@ + + + + ./ + + + diff --git a/vendor/doctrine/doctrine-mongo-odm-module b/vendor/doctrine/doctrine-mongo-odm-module new file mode 160000 index 00000000..3c666736 --- /dev/null +++ b/vendor/doctrine/doctrine-mongo-odm-module @@ -0,0 +1 @@ +Subproject commit 3c666736b68b3233340e8f548b7b0ced9b76c2da diff --git a/vendor/doctrine/doctrine-orm-module/.travis.yml b/vendor/doctrine/doctrine-orm-module/.travis.yml new file mode 100644 index 00000000..c5abeb4c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis.yml @@ -0,0 +1,14 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - composer install --dev --prefer-source + - wget http://cs.sensiolabs.org/get/php-cs-fixer.phar + +script: + - phpunit + - php coverage-checker.php clover.xml 60 + - output=$(php php-cs-fixer.phar fix -v --dry-run --level=psr2 ./src/); if [[ $output ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi; diff --git a/vendor/doctrine/doctrine-orm-module/.travis/Version20120714005702.php b/vendor/doctrine/doctrine-orm-module/.travis/Version20120714005702.php new file mode 100644 index 00000000..31f9413a --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/Version20120714005702.php @@ -0,0 +1,44 @@ +. + */ + +namespace TravisDoctrineMigrations; + +use Doctrine\DBAL\Migrations\AbstractMigration, + Doctrine\DBAL\Schema\Schema; + +/** + * Empty test migration used for testing + */ +class Version20120714005702 extends AbstractMigration +{ + /** + * {@inheritDoc} + */ + public function up(Schema $schema) + { + $this->addSql("SELECT 'Migration succeeded!'"); + } + + /** + * {@inheritDoc} + */ + public function down(Schema $schema) + { + } +} diff --git a/vendor/doctrine/doctrine-orm-module/.travis/application.config.php b/vendor/doctrine/doctrine-orm-module/.travis/application.config.php new file mode 100644 index 00000000..5959d6f0 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/application.config.php @@ -0,0 +1,25 @@ + array( + 'ZendDeveloperTools', + 'Application', + 'DoctrineModule', + 'DoctrineORMModule', + ), + 'module_listener_options' => array( + 'config_glob_paths' => array( + 'config/autoload/{,*.}{global,local}.php', + ), + 'config_cache_enabled' => false, + 'cache_dir' => 'data/cache', + 'module_paths' => array( + './module', + './vendor', + ), + ), + 'service_manager' => array( + 'use_defaults' => true, + 'factories' => array( + ), + ), +); diff --git a/vendor/doctrine/doctrine-orm-module/.travis/dummy-import.sql b/vendor/doctrine/doctrine-orm-module/.travis/dummy-import.sql new file mode 100644 index 00000000..e0ac49d1 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/dummy-import.sql @@ -0,0 +1 @@ +SELECT 1; diff --git a/vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml b/vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml new file mode 100644 index 00000000..4d65757b --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml @@ -0,0 +1,11 @@ + + + doctrine-migrations configuration for travis tests + TravisDoctrineMigrations + + data + diff --git a/vendor/doctrine/doctrine-orm-module/.travis/migrations-execute-config.xml b/vendor/doctrine/doctrine-orm-module/.travis/migrations-execute-config.xml new file mode 100644 index 00000000..8fb62d80 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/migrations-execute-config.xml @@ -0,0 +1,11 @@ + + + doctrine-migrations configuration for travis tests (running preset migrations) + TravisDoctrineMigrations +
+ vendor/doctrine/doctrine-orm-module/.travis + diff --git a/vendor/doctrine/doctrine-orm-module/.travis/run-cli.sh b/vendor/doctrine/doctrine-orm-module/.travis/run-cli.sh new file mode 100644 index 00000000..eb0600cc --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/.travis/run-cli.sh @@ -0,0 +1,17 @@ +./vendor/bin/doctrine-module dbal:import ./vendor/doctrine/doctrine-orm-module/.travis/dummy-import.sql +./vendor/bin/doctrine-module dbal:run-sql "SELECT 1" +./vendor/bin/doctrine-module orm:clear-cache:metadata +./vendor/bin/doctrine-module orm:clear-cache:query +./vendor/bin/doctrine-module orm:clear-cache:result +./vendor/bin/doctrine-module orm:clear-cache:query +./vendor/bin/doctrine-module orm:generate-proxies +./vendor/bin/doctrine-module orm:ensure-production-settings +./vendor/bin/doctrine-module orm:info +./vendor/bin/doctrine-module orm:schema-tool:create +./vendor/bin/doctrine-module orm:schema-tool:update +./vendor/bin/doctrine-module orm:validate-schema +./vendor/bin/doctrine-module orm:run-dql "SELECT COUNT(a) FROM DoctrineORMModuleTest\Assets\Entity\Test a" +./vendor/bin/doctrine-module orm:schema-tool:drop +./vendor/bin/doctrine-module migrations:generate --configuration=vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml +./vendor/bin/doctrine-module migrations:diff --configuration=vendor/doctrine/doctrine-orm-module/.travis/migrations-config.xml +./vendor/bin/doctrine-module migrations:execute 20120714005702 -n --configuration=vendor/doctrine/doctrine-orm-module/.travis/migrations-execute-config.xml \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/LICENSE b/vendor/doctrine/doctrine-orm-module/LICENSE new file mode 100644 index 00000000..516f6816 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2006-2012 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. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/Module.php b/vendor/doctrine/doctrine-orm-module/Module.php new file mode 100644 index 00000000..2f64b1fb --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/Module.php @@ -0,0 +1,7 @@ + array( + 'driver' => array( + // defines an annotation driver with to paths, and names it `my_annotation_driver` + 'my_annotation_driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array( + 'path/to/my/entities', + 'another/path' + ), + ), + + // default metadata driver, aggregates all other drivers into a single one. + // Override `orm_default` only if you know what you're doing + 'orm_default' => array( + 'drivers' => array( + // register `my_annotation_driver` for any entity under namespace `My\Namespace` + 'My\Namespace' => 'my_annotation_driver' + ) + ) + ) + ) +); +``` + +## Connection settings + +Connection parameters can be defined in the application configuration: + +```php + array( + 'connection' => array( + // default connection name + 'orm_default' => array( + 'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'params' => array( + 'host' => 'localhost', + 'port' => '3306', + 'user' => 'username', + 'password' => 'password', + 'dbname' => 'database', + ) + ) + ) + ), +); +``` + +#### Full configuration options + +An exhaustive list of configuration options can be found directly in the Options classes of each module. + + * [DoctrineModule configuration](https://github.com/Doctrine/DoctrineModule/tree/master/src/DoctrineModule/Options) + * [ORM Module Configuration](https://github.com/Doctrine/DoctrineORMModule/tree/master/src/DoctrineORMModule/Options) + * [ORM Module Defaults](https://github.com/Doctrine/DoctrineORMModule/tree/master/config/module.config.php) + +You can find documentation about the module's features at the following links: + + * [DoctrineModule documentation](https://github.com/Doctrine/DoctrineModule/tree/master/docs) + * [DoctrineORMModule documentation](https://github.com/Doctrine/DoctrineORMModule/tree/master/docs) + +## Registered Service names + + * `doctrine.connection.orm_default`: a `Doctrine\DBAL\Connection` instance + * `doctrine.configuration.orm_default`: a `Doctrine\ORM\Configuration` instance + * `doctrine.driver.orm_default`: default mapping driver instance + * `doctrine.entitymanager.orm_default`: the `Doctrine\ORM\EntityManager` instance + * `Doctrine\ORM\EntityManager`: an alias of `doctrine.entitymanager.orm_default` + * `doctrine.eventmanager.orm_default`: the `Doctrine\Common\EventManager` instance + +#### Command Line +Access the Doctrine command line as following + +```sh +./vendor/bin/doctrine-module +``` + +#### Service Locator +To access the entity manager, use the main service locator: + +```php +// for example, in a controller: +$em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default'); +$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager'); +``` \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/UPGRADE.md b/vendor/doctrine/doctrine-orm-module/UPGRADE.md new file mode 100644 index 00000000..2c13126f --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/UPGRADE.md @@ -0,0 +1,19 @@ +# 0.4.0 +Version `0.4.0` has been rewritten from scratch using the new ServiceManager component of ZF2. This allows for +drastically increased performance and reduced complexity of setup. + +An alias has been set to reference Doctrine\ORM\EntityManager to help with BC but it is *deprecated and will be removed* +in the future. The new service name is doctrine.entitymanager.orm_default. + +The module.doctrine_orm.local.config.php.dist has been removed. Please review the +[README.md](http://www.github.com/doctrine/DoctrineORMModule/tree/master/README.md) to setup your connection. + +# 0.3.1 +Version `0.3.1` integrated CLI with composer's CLI scripts registration method. To do so without conflicting with +Doctrine ORM's CLI, you now have to access it either via `./vendor/bin/doctrine-module` or +`./vendor/doctrine/DoctrineModule/bin/doctrine-module`. + +# 0.3.0 +After version `0.2.1`, submodules have been dropped as they were too heavy and complex to manage. If you now want to use +this module, please use composer, as described in +[README.md](http://www.github.com/doctrine/DoctrineORMModule/tree/master/README.md) \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/composer.json b/vendor/doctrine/doctrine-orm-module/composer.json new file mode 100644 index 00000000..699431aa --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/composer.json @@ -0,0 +1,64 @@ +{ + "name": "doctrine/doctrine-orm-module", + "description": "Zend Framework 2 Module that provides Doctrine ORM functionality", + "type": "library", + "license": "MIT", + "keywords": [ + "doctrine", + "orm", + "module", + "zf2" + ], + "homepage": "http://www.doctrine-project.org/", + "authors": [ + { + "name": "Kyle Spraggs", + "email": "theman@spiffyjr.me", + "homepage": "http://www.spiffyjr.me/" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://marco-pivetta.com/" + }, + { + "name": "Evan Coury", + "email": "me@evancoury.com", + "homepage": "http://blog.evan.pro/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + } + ], + "require": { + "php": ">=5.3.3", + "doctrine/doctrine-module": "0.7.*", + "doctrine/orm": ">=2.3-dev,<2.5-dev", + "doctrine/dbal": ">=2.3-dev,<2.5-dev", + "zendframework/zend-stdlib": "2.*", + "zendframework/zend-form": "2.*", + "zendframework/zend-mvc": "2.*", + "zendframework/zend-servicemanager": "2.*" + }, + "require-dev": { + "zendframework/zend-developer-tools": "dev-master", + "doctrine/migrations": "1.*" + }, + "suggest": { + "zendframework/zend-developer-tools": "zend-developer-tools if you want to profile operations executed by the ORM during development", + "doctrine/migrations": "doctrine migrations if you want to keep your schema definitions versioned" + }, + "minimum-stability": "dev", + "autoload": { + "psr-0": { + "DoctrineORMModule\\": "src/", + "DoctrineORMModuleTest\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.8.x-dev" + } + } +} diff --git a/vendor/doctrine/doctrine-orm-module/config/controllers.config.php b/vendor/doctrine/doctrine-orm-module/config/controllers.config.php new file mode 100644 index 00000000..e5aadcba --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/config/controllers.config.php @@ -0,0 +1,40 @@ +. + */ + +return array( + 'factories' => array( + // Yuml controller, used to generate Yuml graphs since + // yuml.me doesn't do redirects on its own + 'DoctrineORMModule\\Yuml\\YumlController' => function (\Zend\ServiceManager\AbstractPluginManager $pluginManager) { + $config = $pluginManager->getServiceLocator()->get('Config'); + + if ( + !isset($config['zenddevelopertools']['toolbar']['enabled']) + || !$config['zenddevelopertools']['toolbar']['enabled'] + ) { + // prevent instantiation if the toolbar is not enabled + return null; + } + + return new \DoctrineORMModule\Yuml\YumlController( + new \Zend\Http\Client('http://yuml.me/diagram/class/', array('timeout' => 30)) + ); + }, + ), +); diff --git a/vendor/doctrine/doctrine-orm-module/config/module.config.php b/vendor/doctrine/doctrine-orm-module/config/module.config.php new file mode 100644 index 00000000..c7e66c3e --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/config/module.config.php @@ -0,0 +1,182 @@ +. + */ + +return array( + 'doctrine' => array( + 'connection' => array( + // Configuration for service `doctrine.connection.orm_default` service + 'orm_default' => array( + // configuration instance to use. The retrieved service name will + // be `doctrine.configuration.$thisSetting` + 'configuration' => 'orm_default', + + // event manager instance to use. The retrieved service name will + // be `doctrine.eventmanager.$thisSetting` + 'eventmanager' => 'orm_default', + + // connection parameters, see + // http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html + 'params' => array( + 'host' => 'localhost', + 'port' => '3306', + 'user' => 'username', + 'password' => 'password', + 'dbname' => 'database', + ) + ), + ), + + // Configuration details for the ORM. + // See http://docs.doctrine-project.org/en/latest/reference/configuration.html + 'configuration' => array( + // Configuration for service `doctrine.configuration.orm_default` service + 'orm_default' => array( + // metadata cache instance to use. The retrieved service name will + // be `doctrine.cache.$thisSetting` + 'metadata_cache' => 'array', + + // DQL queries parsing cache instance to use. The retrieved service + // name will be `doctrine.cache.$thisSetting` + 'query_cache' => 'array', + + // ResultSet cache to use. The retrieved service name will be + // `doctrine.cache.$thisSetting` + 'result_cache' => 'array', + + // Mapping driver instance to use. Change this only if you don't want + // to use the default chained driver. The retrieved service name will + // be `doctrine.driver.$thisSetting` + 'driver' => 'orm_default', + + // Generate proxies automatically (turn off for production) + 'generate_proxies' => true, + + // directory where proxies will be stored. By default, this is in + // the `data` directory of your application + 'proxy_dir' => 'data/DoctrineORMModule/Proxy', + + // namespace for generated proxy classes + 'proxy_namespace' => 'DoctrineORMModule\Proxy', + + // SQL filters. See http://docs.doctrine-project.org/en/latest/reference/filters.html + 'filters' => array() + ) + ), + + // Metadata Mapping driver configuration + 'driver' => array( + // Configuration for service `doctrine.driver.orm_default` service + 'orm_default' => array( + // By default, the ORM module uses a driver chain. This allows multiple + // modules to define their own entities + 'class' => 'Doctrine\ORM\Mapping\Driver\DriverChain', + + // Map of driver names to be used within this driver chain, indexed by + // entity namespace + 'drivers' => array() + ) + ), + + // Entity Manager instantiation settings + 'entitymanager' => array( + // configuration for the `doctrine.entitymanager.orm_default` service + 'orm_default' => array( + // connection instance to use. The retrieved service name will + // be `doctrine.connection.$thisSetting` + 'connection' => 'orm_default', + + // configuration instance to use. The retrieved service name will + // be `doctrine.configuration.$thisSetting` + 'configuration' => 'orm_default' + ) + ), + + 'eventmanager' => array( + // configuration for the `doctrine.eventmanager.orm_default` service + 'orm_default' => array() + ), + + // collector SQL logger, used when ZendDeveloperTools and its toolbar are active + 'sql_logger_collector' => array( + // configuration for the `doctrine.sql_logger_collector.orm_default` service + 'orm_default' => array(), + ), + + // entity resolver configuration, allows mapping associations to interfaces + 'entity_resolver' => array( + // configuration for the `doctrine.entity_resolver.orm_default` service + 'orm_default' => array() + ), + + // authentication service configuration + 'authentication' => array( + // configuration for the `doctrine.authentication.orm_default` authentication service + 'orm_default' => array( + // name of the object manager to use. By default, the EntityManager is used + 'objectManager' => 'doctrine.entitymanager.orm_default', + //'identityClass' => 'Application\Model\User', + //'identityProperty' => 'username', + //'credentialProperty' => 'password' + ), + ), + ), + + //////////////////////////////////////////////////////////////////// + // `zendframework/zend-developer-tools` specific settings // + // ignore these if you're not developing additional features for // + // zend developer tools // + //////////////////////////////////////////////////////////////////// + + 'router' => array( + 'routes' => array( + 'doctrine_orm_module_yuml' => array( + 'type' => 'Zend\\Mvc\\Router\\Http\\Literal', + 'options' => array( + 'route' => '/ocra_service_manager_yuml', + 'defaults' => array( + 'controller' => 'DoctrineORMModule\\Yuml\\YumlController', + 'action' => 'index', + ), + ), + ), + ), + ), + + 'view_manager' => array( + 'template_map' => array( + 'zend-developer-tools/toolbar/doctrine-orm-queries' => __DIR__ . '/../view/zend-developer-tools/toolbar/doctrine-orm-queries.phtml', + 'zend-developer-tools/toolbar/doctrine-orm-mappings' => __DIR__ . '/../view/zend-developer-tools/toolbar/doctrine-orm-mappings.phtml', + ), + ), + + 'zenddevelopertools' => array( + 'profiler' => array( + 'collectors' => array( + 'orm_default' => 'doctrine.sql_logger_collector.orm_default', + 'orm_default_mappings' => 'doctrine.mapping_collector.orm_default', + ), + ), + 'toolbar' => array( + 'entries' => array( + 'orm_default' => 'zend-developer-tools/toolbar/doctrine-orm-queries', + 'orm_default_mappings' => 'zend-developer-tools/toolbar/doctrine-orm-mappings', + ), + ), + ), +); diff --git a/vendor/doctrine/doctrine-orm-module/config/services.config.php b/vendor/doctrine/doctrine-orm-module/config/services.config.php new file mode 100644 index 00000000..c3f34d15 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/config/services.config.php @@ -0,0 +1,47 @@ +. + */ + +return array( + 'aliases' => array( + 'Doctrine\ORM\EntityManager' => 'doctrine.entitymanager.orm_default', + ), + 'factories' => array( + + 'doctrine.authenticationadapter.orm_default' => new DoctrineModule\Service\Authentication\AdapterFactory('orm_default'), + 'doctrine.authenticationstorage.orm_default' => new DoctrineModule\Service\Authentication\StorageFactory('orm_default'), + 'doctrine.authenticationservice.orm_default' => new DoctrineModule\Service\Authentication\AuthenticationServiceFactory('orm_default'), + + 'doctrine.connection.orm_default' => new DoctrineORMModule\Service\DBALConnectionFactory('orm_default'), + 'doctrine.configuration.orm_default' => new DoctrineORMModule\Service\ConfigurationFactory('orm_default'), + 'doctrine.entitymanager.orm_default' => new DoctrineORMModule\Service\EntityManagerFactory('orm_default'), + + 'doctrine.driver.orm_default' => new DoctrineModule\Service\DriverFactory('orm_default'), + 'doctrine.eventmanager.orm_default' => new DoctrineModule\Service\EventManagerFactory('orm_default'), + 'doctrine.entity_resolver.orm_default' => new DoctrineORMModule\Service\EntityResolverFactory('orm_default'), + 'doctrine.sql_logger_collector.orm_default' => new DoctrineORMModule\Service\SQLLoggerCollectorFactory('orm_default'), + 'doctrine.mapping_collector.orm_default' => function (Zend\ServiceManager\ServiceLocatorInterface $sl) { + $em = $sl->get('doctrine.entitymanager.orm_default'); + + return new DoctrineORMModule\Collector\MappingCollector($em->getMetadataFactory(), 'orm_default_mappings'); + }, + 'DoctrineORMModule\Form\Annotation\AnnotationBuilder' => function(Zend\ServiceManager\ServiceLocatorInterface $sl) { + return new DoctrineORMModule\Form\Annotation\AnnotationBuilder($sl->get('doctrine.entitymanager.orm_default')); + }, + ), +); diff --git a/vendor/doctrine/doctrine-orm-module/coverage-checker.php b/vendor/doctrine/doctrine-orm-module/coverage-checker.php new file mode 100644 index 00000000..25371807 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/coverage-checker.php @@ -0,0 +1,60 @@ +. + */ + +/** + * Code coverage checker. Analyzes a given `clover.xml` report produced + * by PHPUnit and checks if coverage fits expected ratio + * + * Usage: + * php coverage-checker + * + * @author Marco Pivetta + */ + +$inputFile = $argv[1]; +$percentage = min(100, max(0, (int) $argv[2])); + +if (!file_exists($inputFile)) { + throw new InvalidArgumentException('Invalid input file provided'); +} + +if (!$percentage) { + throw new InvalidArgumentException('An integer checked percentage must be given as second parameter'); +} + +$xml = new SimpleXMLElement(file_get_contents($inputFile)); +/* @var $metrics SimpleXMLElement[] */ +$metrics = $xml->xpath('//metrics'); + +$totalElements = 0; +$checkedElements = 0; + +foreach ($metrics as $metric) { + $totalElements += (int) $metric['elements']; + $checkedElements += (int) $metric['coveredelements']; +} + +$coverage = round(($checkedElements / $totalElements) * 100); + +if ($coverage < $percentage) { + echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL; + exit(1); +} + +echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL; diff --git a/vendor/doctrine/doctrine-orm-module/docs/EXTRAS_ORM.md b/vendor/doctrine/doctrine-orm-module/docs/EXTRAS_ORM.md new file mode 100644 index 00000000..23fe5a34 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/docs/EXTRAS_ORM.md @@ -0,0 +1,116 @@ +# Extra goodies included with DoctrineModule +The items listed below are entirely optional and are intended to enhance integration between Zend Framework 2 and +Doctrine 2 . + +## ObjectExists and NoObjectExists Validators +The ObjectExists and NoObjectExists are validators similar to Zend\Validator\Db validators. You can +pass a variety of options to determine validity. The most basic use case requires an entity manager (em), +an entity, and a field. You also have the option of specifying a query_builder Closure to use if you +want to fine tune the results. + +```php + $serviceLocator->get('Doctrine\ORM\EntityManager')->getRepository('My\Entity\User'), + + // fields to match + 'fields' => array('username'), +)); + +// following works also with simple values if the number of fields to be matched is 1 +echo $validator->isValid(array('username' => 'test')) ? 'Valid' : 'Invalid! Duplicate found!'; +``` + +## Authentication adapter for Zend\Authentication +The authentication adapter is intended to provide an adapter for `Zend\Authentication`. It works much +like the `DbTable` adapter in the core framework. You must provide the entity manager instance, +entity name, identity field, and credential field. You can optionally provide a callable method +to perform hashing on the password prior to checking for validation. + +```php +getLocator()->get('Doctrine\ORM\EntityManager'), + 'Application\Test\Entity', + 'username', // optional, default shown + 'password', // optional, default shown, + function($identity, $credential) { // optional callable + return \My\Service\User::hashCredential( + $credential, + $identity->getSalt(), + $identity->getAlgorithm() + ); + } +); + +$adapter->setIdentityValue('admin'); +$adapter->setCredentialValue('pa55w0rd'); +$result = $adapter->authenticate(); + +echo $result->isValid() ? 'Authenticated!' : 'Could not authenticate'; +``` + +## Custom DBAL Types +To register custom Doctrine DBAL types, simply add them to the `doctrine.configuration.my_dbal_default.types` +key in you configuration file: + +```php + array( + 'configuration' => array( + 'my_dbal_default' => array( + 'types' => array( + // You can override a default type + 'date' => 'My\DBAL\Types\DateType', + + // And set new ones + 'tinyint' => 'My\DBAL\Types\TinyIntType', + ), + ), + ), + ), +); +``` + +You are now able to use them, for example, in your ORM entities: + +```php + array( + 'connection' => array( + 'orm_default' => array( + 'doctrine_type_mappings' => array( + 'tinyint' => 'tinyint', + ), + ), + ), + ), +); +``` + +Now using Schema-Tool, whenever it detects a column having the "tinyint" it will convert it into a "tinyint" +Doctrine Type instance for Schema representation. Keep in mind that you can easily produce clashes this +way, each database type can only map to exactly one Doctrine mapping type. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/docs/configuration.md b/vendor/doctrine/doctrine-orm-module/docs/configuration.md new file mode 100644 index 00000000..564da776 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/docs/configuration.md @@ -0,0 +1,175 @@ +#### How to Register Custom DQL Functions + +```php +return array( + 'doctrine' => array( + 'configuration' => array( + 'orm_default' => array( + 'numeric_functions' => array( + 'ROUND' => 'path\to\my\query\round' + ) + ) + ), + ), +) +``` + +#### How to use Memcache + +```php +'doctrine' => array( + 'configuration' => array( + 'orm_default' => array( + 'metadata_cache' => 'my_memcache', + 'query_cache' => 'my_memcache', + 'result_cache' => 'my_memcache', + ) + ), +); +``` + +```php +'service_manager' => array( + 'factories' => array( + 'doctrine.cache.my_memcache' => function ($sm) { + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $memcache = new \Memcache(); + $memcache->connect('localhost', 11211); + $cache->setMemcache($memcache); + return $cache; + }, + ), +), +``` + +### How to register type mapping + +```php +'doctrine' => array( + 'connection' => array( + 'orm_default' => array( + 'doctrine_type_mappings' => array( + 'enum' => 'string' + ), + ) + ) +), +``` + +### How to add new type + +```php +'doctrine' => array( + 'configuration' => array( + 'orm_default' => array( + 'types' => array( + 'mytype' => 'Application\Types\MyType' + ) + ) + ), +), +``` + +```php +'connection' => array( + 'orm_default' => array( + 'doctrine_type_mappings' => array( + 'mytype' => 'mytype' + ), + ) +), +``` + +### How to Use Two Connections + +```php +'doctrine' => array( + 'connection' => array( + 'orm_crawler' => array( + 'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'params' => array( + 'host' => 'localhost', + 'port' => '3306', + 'user' => 'root', + 'password' => 'root', + 'dbname' => 'crawler', + 'driverOptions' => array( + 1002 => 'SET NAMES utf8' + ), + ) + ) + ), + + 'configuration' => array( + 'orm_crawler' => array( + 'metadata_cache' => 'array', + 'query_cache' => 'array', + 'result_cache' => 'array', + 'driver' => 'orm_crawler', + 'generate_proxies' => true, + 'proxy_dir' => 'data/DoctrineORMModule/Proxy', + 'proxy_namespace' => 'DoctrineORMModule\Proxy', + 'filters' => array() + ) + ), + + 'driver' => array( + 'Crawler_Driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array( + __DIR__ . '/../src/Crawler/Entity' + ) + ), + 'orm_crawler' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\DriverChain', + 'drivers' => array( + 'Crawler\Entity' => 'Crawler_Driver' + ) + ), + ), + + 'entitymanager' => array( + 'orm_crawler' => array( + 'connection' => 'orm_crawler', + 'configuration' => 'orm_crawler' + ) + ), + + 'eventmanager' => array( + 'orm_crawler' => array() + ), + + 'sql_logger_collector' => array( + 'orm_crawler' => array(), + ), + + 'entity_resolver' => array( + 'orm_crawler' => array() + ), + + ), +``` + +Module.php +```php +public function getServiceConfig() +{ + return array( + 'factories' => array( + 'doctrine.connection.orm_crawler' => new DBALConnectionFactory('orm_crawler'), + 'doctrine.configuration.orm_crawler' => new ORMConfigurationFactory('orm_crawler'), + 'doctrine.entitymanager.orm_crawler' => new EntityManagerFactory('orm_crawler'), + + 'doctrine.driver.orm_crawler' => new DriverFactory('orm_crawler'), + 'doctrine.eventmanager.orm_crawler' => new EventManagerFactory('orm_crawler'), + 'doctrine.entity_resolver.orm_crawler' => new EntityResolverFactory('orm_crawler'), + 'doctrine.sql_logger_collector.orm_crawler' => new SQLLoggerCollectorFactory('orm_crawler'), + + 'DoctrineORMModule\Form\Annotation\AnnotationBuilder' => function(ServiceLocatorInterface $sl) { + return new AnnotationBuilder($sl->get('doctrine.entitymanager.orm_crawler')); + }, + ), + ); +} +``` \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/docs/developer-tools.md b/vendor/doctrine/doctrine-orm-module/docs/developer-tools.md new file mode 100644 index 00000000..bf593263 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/docs/developer-tools.md @@ -0,0 +1,110 @@ +# Zend Developer Tools in DoctrineORMModule + +If you ever tried [Zend Developer Tools](https://github.com/zendframework/ZendDeveloperTools) you will surely understand +the importance of being able to track performance pitfalls or excessive amount of queries in your applications when +developing. + +## Setup + +To setup [Zend Developer Tools](https://github.com/zendframework/ZendDeveloperTools), run + +```sh +php composer.phar require zendframework/zend-developer-tools +``` + +Then enable `ZendDeveloperTools` in your modules and enable profiling and the toolbar (see docs of Zend Developer Tools +for that). + +Once `ZendDeveloperTools` is enabled, having `doctrine.entity_manager.orm_default` as your default `EntityManager`, you +will notice that the queries performed by the ORM get logged and displayed in the toolbar. + +![](http://github.com/doctrine/DoctrineORMModule/raw/master/docs/images/zf2-zend-developer-tools-doctrine-module.png) + +## Customization + +If you want to customize this behavior (or track multiple `EntityManager` instances) you can do it in different ways. +Please note that if you have set an `SQLLogger` in your configuration, this functionality won't override it, so you can +use these features in total safety. + +### Multiple EntityManager/Connection instances and logging + +*WARNING! These are advanced features! Even if the code is fully tested, this is usually not required for most users!* + +To setup logging for an additional DBAL Connection or EntityManager, put something like following in your module: + +```php + array( + 'sql_logger_collector' => array( + 'other_orm' => array( + // name of the sql logger collector (used by ZendDeveloperTools) + 'name' => 'other_orm', + + // name of the configuration service at which to attach the logger + 'configuration' => 'doctrine.configuration.other_orm', + + // uncomment following if you want to use a particular SQL logger instead of relying on + // the attached one + //'sql_logger' => 'service_name_of_my_dbal_sql_logger', + ), + ), + ), + + 'zenddevelopertools' => array( + + // registering the profiler with ZendDeveloperTools + 'profiler' => array( + 'collectors' => array( + // reference to the service we have defined + 'other_orm' => 'doctrine.sql_logger_collector.other_orm', + ), + ), + + // registering a new toolbar item with ZendDeveloperTools (name must be the same of the collector name) + 'toolbar' => array( + 'entries' => array( + // this is actually a name of a view script to use - you can use your custom one + 'other_orm' => 'zend-developer-tools/toolbar/doctrine-orm', + ), + ), + ), + ); + } + + public function getServiceConfiguration() + { + return array( + 'factories' => array( + // defining a service (any name is valid as long as you use it consistently across this example) + 'doctrine.sql_logger_collector.other_orm' => new \DoctrineORMModule\Service\SQLLoggerCollectorFactory('other_orm'), + ), + ); + } + + public function onBootstrap(\Zend\EventManager\EventInterface $e) + { + $config = $e->getTarget()->getServiceManager()->get('Config'); + + if ( + isset($config['zenddevelopertools']['profiler']['enabled']) + && $config['zenddevelopertools']['profiler']['enabled'] + ) { + // when ZendDeveloperTools is enabled, initialize the sql collector + $app->getServiceManager()->get('doctrine.sql_logger_collector.other_orm'); + } + } +} +``` + +This example will simply generate a new icon in the toolbar, with the log results of your `other_orm` connection: + + +![](http://github.com/doctrine/DoctrineORMModule/raw/master/docs/images/zend-developer-tools-multiple-entity-managers.png) diff --git a/vendor/doctrine/doctrine-orm-module/docs/images/zend-developer-tools-multiple-entity-managers.png b/vendor/doctrine/doctrine-orm-module/docs/images/zend-developer-tools-multiple-entity-managers.png new file mode 100644 index 0000000000000000000000000000000000000000..43ff2af9a58fe4196447eccb75c6e9150f9205ea GIT binary patch literal 27793 zcmc$`Wmp_fzb!hrdvJ#U!Gi}UXmB0e-QAra2@u?6aCdi@K!OeK?(P~~4*BnQ-+T8y z@4nCZcIv}SO;2}MKh;&gUca?gb-0p(6dE!SG5`QTll~&E0sug_zSYMOq2E4#Xr5}m zeZjbhN~<9vA}(zxuD|^xbd}I_Rdq0T^)PZa16bHQ*qJfAm^ho6*}GUexSqj)g#Z8w zfVB8$HP6iB6%WmK8gqbij`~ou15&8k+bTu|Du#I!_g;G?xnn&XooPiATg~{AiIUpp z_cswMmdB)Oum!hGC`%r@{sBy&1-&1%m+@CVp1!7OtuCjaRt}5P;Zda!Op#Zb%l7&Q zd)vB4B_+4+J@lQkGFYGpV(u&D^&UEaG6Yi;muMFm@vXuh)?+_eujENU%4GNXEo|o zA)V-ubzS^dx1xufi|E%2Nb9%z8b3HjpBgJ)MTa%|xq|Fl#>l6!HdnRU^GrSkz44CZ zbC>=L3@H_-vis(=W=&sOSN-nXFllfb)!idOfUqKE0Tkllm~cyBIcd_XMg(0Qwy7TI zbta~6!v@3FYS_oc^I}2xH|cK^N!)m8iuyaYr0MOB1s}|3}~3(NAQk$rVfS6 zi{@`OmSZKCJ{k>L%L<SMc{x zpR0XT2=yf#|2)|=RCE8BC95^wdq$2`bKDcY6Ql?KikUm$TVr!G&SFQS4?>Vz^<`H6 zRRGEw?^68IBGhjlA02f=*74Zc^Z`3Bg7?)=srm++^)>ejBW&jY#%KpCpn_m-NQ%m8 zj$^-_V?qY{XTi!Mdtr99IMu}|7u&XEdbdkePfuFGuY4a?z5w%)kih8pS z)k{0zG+E2e))D}aF|@L=(HNLr%q)&T^|M-anZ;ktYMMQ@lTtho7jDI_WMGO0RxbQ$ zm{CQ8$x%=&>)dsU>?a}(q@`|M4dc;CaKST>_5s>{{AT%Xjof$Fi#2AKu`*e^JUM>8sC zm(7;JlJwbx=!~Jo+-LlPKHV)!{=`8?Upx5hE{I>{W!y|{tRJEc2N2n$!g*>EE}ZMM z^rN)9+Snu2KSBj%i+2qHrm?Me47K2Cu|7W{hTY)nwGFL^& zfUL)Pi%Ce}{P56A+FhlAHztH>Gi5JAeGtW1O9U`fambD?Wh-g7n6M#Jb8sKky(r#G z6ilfmd1~88cxK&(4NQ7z^gc`$U&HOM*5efbPt?z-@(8OAylB^7Ct4ogTxpEikxPS~ zz~os;78P;Q;JRo3U!mVv4OU4_-Q2dKQ|>vi%zo$csz&n-FSIsfFp@h*;j@%*TOTDu zjoMTD^LEKmnX8N?Rqgsj<%aX>B-MY-l=?*b{7`lvZ1G3y>UsQ7Qo%%Ae)n{ZzcT2H zLE~L9mUW6Gi$tXyiqAA&M`Wx8%;1+~8s&3Z6&ASoIRNlCa@hbbChA{B7I_rbri+lI1nzbH*w!P6k$1&qIdbUfmNeNHVX=^no z!h@0f>JakUuDw&9J*L<0Ybx=0+s>`al17(wa!32{DR7W|X!Aq8+$}3|sjd7L3B2ut zQ%lCs&l9>i4<@#%pM;g&O)~9H><;Hm_4v0xIKT~;k^fc+9+G+R$%i{JJ!2t=p=ZF#*)|+xfjhQ*@7WHT4(gWMIRZ-W4&7 z0$5Fm-TbDsppLr5DVg7fkpF3H(Wv^3o3}#7s=)p4gIjgG2cF?pcN%pVImpP=gsQzoOV~> ztc( z0Pp~VY@t$3POmy*X@cJ^+gz4h|J97u0efeu(*M95`G*P%VHs>17SLTGf4Alt!o z1>{06E-$RiXaj8Zw?o~!w*qjZ^aM&YCE;B`i|XoDysTWuS6}2gSfyg*tWv}L9u#(z z=N1z*Y{U6x6zNt+rE6DLe&tHmj-i=k3y#TEE4%rbxZuatm>iYW+tCBQ#3=C6JKqlu zm8E|-09P$x{8>;nlF2w!s7zV_o8cI86&sXgG}ctAo4@|%sYkhJtaKQy)~koiCU@Ax zxNuzFTEBnMThz;7$k5UBOufk9zSKO*UA!Xj}A?jRs#O%c`UycjdRofwEouWbQ{g&iPrTV|&TSP+yb? z`MKEdH4PS{_BKT6?GX~4OS7Mr7iN{gsq9BRF+ zMEG1j=3z9MDsCQF(v2GGS_-t_75&MJ;j~|h)-J%e+dsBXi|7|KiyRS z95iw=OiJW&={#*pPjI?Y+9qd+>%H`uTv}}i%zJ-ozs>KKXxD!sxN4yLNbz829@26G zi$N(+0E?dG?h61!6v-Zoy76AL8Y_C#`YSSDpPy_&0p!pLvS`THs*1|Lydvb<_|ZA@ z(4^#k$W>)VfC`jMj%Tb;Sy2!8k^9?zhIvPteYR^PP}0U-SMjdrX?0<{&%_U88TgfD zV7uspowd*Rt4Pn&Ou{g{fC}ZvoSB9qNY9lkch;!6 zwhtd{d0Dx(OY7t&MXb=uv-Dp{YoB#*nrSBNZFb~@0Z>3v$18{CGL;*U79FW-qFe?o ze@D9uVS2FXl@4QGUp4uVk_FI+#l`m>?b?~E+p@dakJ9k$k-7F=YFLBS&38OU=mz6z z(G^5xpB)4~b1)vl#xj*E#3da+n^^n9xmD!Q4ryz`X-qH&ls^By5!tyMT*gq6g%Bz`o}*a%~a7g>59+w}l40 zr`EABb1+XbfUEC{8UawmnCKDczJ*R);yrt*t3DBj&EwY458C+TZB_@N=W>wQ{Pe;f ziKYH&3-UKkQ)bB*^f$!GJ1h&s<5>un9E==9fDaKxcX*p$y;T7_^Gn+ZVXdNtv{iGB z{--yQ8lJWZp)*Hbv%~j0mu6g6_ww>s+W@7+peT^V4wbrqA`tsyF<^Rv&f`_fZ2kih zK^jdq3qON|#33?0z|LBnZ~l|cU~j#IF*)GfMeE>vJrzf@5wkOt{2 zu`!H!iL^s5&e3752d+M8Mq${&)SP0RN^PM)YMsw^B!ZWPCn2mt+hfo{)xjc`?ssCl zBg_mZo@lB1PK9A3wneS@z5Cg5?_AmFJWq9}Ua209ow-5pNawa=J?-xP@dX&UFD#R2 zW;o1-b3#sFI8+NetCtQ>nE(3`-{H%dTXMXny-Pvvyi$8w|Ub}EyB0>qgyZrxEaMx0I#8()HsZG5d% zW*GFCKSfM^P;=t59EJgK3jxKn5Kkrg@IM4FG@o>;U}lKA6BtTvi@sYJ?g90CboFb}nmlwd!wVX-O$p#Y;A* zbvkv2E+;ycClPE0WVqQ4W>T{Zxs&Xx0p%D^M^4(ihB*t(E#&`PTqbJqp`Nm%sjdC= z4|a*%Ve*Pz`F83BfYde*VxU%kRxb+CrZj zN+{2NKL*l1lO8A%uGbo1*4W@HOlQ-T9}VYqQdiQbFq$7GE-$u|Kd!NqCZ}s;GO*8r zCE7E1d#I?frWB#D|($#X9B3tOd`wFmIt5=+YLvUE=$#1D4L``4y2s@XUA z$-W)}vPciLp-;v;Kq%)#?mU8I^d)31FYPS!OW^&@C6hj__H{Ro4B{4Uorp9Y@ap!! zw9pDXYwO0l4kM>xY>Q@rIid1dgq{n+PJTCHu;4<#PG4DdyEa4HU+I7xXW#8LChp(B{0pOGHo(0nlQH^T#JQth?R_~IJYJUe+$}q5d z(_#U_L&stfbbUDaS?>wi4PEnj`KN@F9i-mTYIer~`VkkAp!OoEY+HO3cyb6cU212K zmRSBx9+>kUjmfrUiv#6QzgLjTUiw1kh7-wRjtQ+bQ{@oR^7PodC`Ep|viVdvL{Yj{ zf9kg!qKMA?DbY5SKdzp`)m71+vw*LSLunrjK--pUBKY?>cfxY(SV-lHU{Tz({ZbZ> z5NqX3?F|OkUFZ_~+TzBzh{fgmNtY>?Y+{86aK7_>9vcBQg%9{T##=lVqe$u>y-e+>uN7%~FZdGFcohzKY*}&uad+)M z%hb4v%%Jx+7a1RYWpCBR!F?V6>7WAA;b?!n!$@`M^BpiHUy0+j z$tG;IfVEWR$9zagvdVc35Rpeqe4?azSDkwCcZl67SYHngoQ)o~*AAYFeV!R|A|(b2 zE#7|X@>#F9Ott83Z77YG36SMEGSD$a0%+eSf|OhRh|kJpG=3_*lD;lPT7jW36g6zj0U9|@PbJWA_xz`8^StH*AtGqP=4&6ssb6v zZtkb%odR|{W|iFCXY1R%M@u2E1}zg9xe;*xNe0eQ{2!8pfspvMv{l}7!ye&lM;_XP z^Y3r*hS)pz4E#g>(WD_nY&`T@Jo;1P8=G}M(y0+WrIrNx#PN^*C~9{I#c591xI1crt+NAgd&xyRTD3?XL5zQs~vpm2+;4M z8BsoV9&Za2N2}s<^4ulffVpTG@a=j|%AH2$vY$tn9$Y#(u;vbgAsxM&9JyD>=(lnY zGq~Mr{dbRE$dqUzN&zq3%z7ut>VmyET1a(Oqpycj3+IgpO`N-(yd9pW6+0~8%Cp_? z7C&EMZD(YnO14<4G6=?N8^t$gtj3S^6wR1EN`I5O)pvavPDg?wrK#k{FmUJAhY?X@ z+XG`9&ggIM2)n*?*K|zY^Cq}@x>kFAa~~sLEUZjft0|<*)?H$+t*#c|wNU{G?M&Cv6XXzAKp#pP6;Y!s0D1YYbeYb?r|Fzi4JD)v_ezv2kEdCrNk7t?frBZ z`)wm+s|AqT4c4pw=5ED!aVTJTxoZiAWz`kuIW8;RpnQ%CP?Zw4o5MW!YDe3-*r`dZUv{i>WWYDT=Mi5>FGVmJM{hItr+Q@@i@>9P)u^`{MT=O913BZsHzwH>=ffd% ze+~>`#UwMyOr6!@m2o{~YrEwxBmI-Ms5RdgCsXUg6A3+jh2=jVrPH5Z79S{2ysP}F zEaV%KpI!tE*1T?adV4+)7t@;GVuE=C2E&hUj-&nS12AMqq`mwNjS;}TG21kejIW8A z<)z}vwU1IwXZ25_rbV0=)!BUwHZu(SDouTAeyx!}A%lhbvoJ?sDI~*IkY0oNlLqWw zvQo!W;=YMlGcxU-B2Kv+Zc~>-HE=BH_fO|YRw_L`PDYPMd>e$(uy|qJ$;N(&1+wUO zTJOCabeyq54vC}0dK{;!qD|nsZCVFE$5P`ZQ3D?*A0yq%sG`Y|N0tY5`|m?mJO+zi zS-GK^xS66R*r}-3v&lj|z3E1`GoVM^HCk@;U8zI6(;j;XgeH|_;0Q0{SB)C{~qvp+Ng7q4D zJ~ng7)VtBw>z=4ASX@LLDw`B_l~Y}y_7&?(!wmNy3q*w@;OO5SxI2@3j#=|t#))Ci2in}q$PigCQ>-*)&DsQBNT6U{hRkNOQhK)YU z$u7{bUUVt&%%CKY+r!bS{z#mgaxAc3rT z5djU81#-aUten$te`L+4ox41;YPSlYBsUw*wOi~iCV~;nuk|Tz_dj|r|8^3rXstHM z$ediah7=El4>g`wsEq%J<|2W0dLWFNP2p0uO@k0I%l}qJ;SgiYBj`zHF&JzFTCN5B zWZ-Cp@{PD@ww#yB)AHl>j^Cf{JvN`xUYY>VY|^iUqgK(vL}-UOXiNbU*KT!>_~Y8? zTT$nojx*5IJ931UNf)N!`+Un@E|ESG&Ve(!nCs_?V1l@0D`@_c6Zxia!Avp&0DNbs zbv?S|vUq3rl{*vBcI!uT{FBK?jgsH<6*jWFBu-jQTO!3}#@bE}_G^FO0vOyv_7+-n zWTw<)%P^+p%iGTMJU++Pi1dCOxj?*`u0TW^AEKLk6~U4{^tRT>SsBe1HzUtVf< z?6x&)#JM6)dyb-yswjGT&~A;mqEbyP2>%W}Zu;BQ3Z;ibWv%Sc#w7|*r>U+_h8pP% zGHo0U%EeXomi?e5)UVE#H;Yigo0Nra@BXUT?XJaIhaW{Fi0Ajna~$-T4A7{KFF4-BzED- zD5_bAkbgrU79H)BACva>b$ssj+!c%25vUFR@*r@npQ#JF-(J7rXU7v!>YV#&Z6y5S zNzW%0cfYxROG#$}n-{aT?a2cA#wC4c_Iy@7c~wWv`ciXHY6A}362R`FVCOPtg+d1klgl$+#FUex9=V5jn@&@wZ49rRegE`qTW=my0{|79>H{&k%34}Z9{yT> za;JjJLrvG4*SYBxpFG|I1r5PIE3M5_cx@Vwy5ds$ywOCwkb<>V*RnuTu3gf$&|()y z*+}NWOSIQu_}?N;&r5VE-OkGp(keqcmopKihGWzB;!h#`<`n>oZKacfXex1oDxauOz-PQ)L4nmLXKVR>?wc zSljMyQk`bmM%Cp1(+j}jxiY+)y=fb|w`ei*Ao58l-=o-mtC{Yxdihh6dpI?m-B~lI zY3Ba%T<}vHJWKnjfYp>hIGiQ+Xq8smh4x~xr#?>l&wlN&EbV%BkaX4~ z7gUBs3z?~MX-${w2M51Ap6JUntPY(88%H-6GVpI+W(~i}mPj+*PVMkf`F-$yZg(-P zW0LuZ7Ro4YcPq`i{#Kx4H9L%)J)mgGKyx0QLF`0PiwnhaaEQY}I47+z_e%svYdUJ< zkN-W*jkg$+$YqdI&n%}Vp8pwPheSSo_e!vnfs;}%;_Y^j!{Q&xhfv`^+Sm^b`0ttK zp{l!!8tj^Ml#)cTbejv!WT)!4Lt|u-Kh7gRc`uWS)f+fpmz{v#5mIY620aE>kMVdr z(oV0d*70kg84r_$m2Y3mw)DJ8pno~Rg)j)*U+KweJl*icnBh+B-Ri1v{i!zF*TLVP zq9C%;zDon~guK8ICwefUYT%T0oUclTFz^|jIF%O{F=Z^6_>kEFD_2_n7Vavm%poBG~_Cl?eQ2`$r9tHwQ&5TPh>P8#)^x z7{-0c|10CaB|gOfrB2NmMvCo5B0y@vFKRydR*;2f6GnP!B#VDQ&f1T5=c%25K<5#f z|1egB#Q3IXgx|ed71=<{L9abL`=WUMok*0|2kTdFXZ2JdEoQOQmqiSW5tVo|bG(aY zRMT&3=@jdfB2_ep;#6LGlzmSfP0I#Y+y}vQ_P^Xb+%z>x*9Ewy&_Pss`eq{BJ zS`j@^NJCj}v65N!;a=uF_L)A8n0)~oi83y;D6Y%PV~+j_jsA<+9y#jJ6j;7Uu2ydG z0O{m0B;;spHm$%UzZ2oiznTk(PkYr}wIJ5skmTt`_R;IAQ5+5Genm^K|CAs1(o6(F zdWq%N?wJpZS&%fJK->SH!ko)lNtPC5>+>1tDj8T2d)j@Xp%cINH5=<9?g55i z{(B%T^UliJn09aM-@)Wa+?=Q-o{Fn$ey>=I;x!vO4~+-UCkxGKliU41UDsAK*84%8TvD~I zr53?{5BC1$v?2T0F+Z*eoj!c-Ck-s3@YjQsvhv@J2s{Y@lL?*dy|UfKJS0l5PKRyh zAU5NNkZuF9kc&?R((}M3sp?otK}ASgUm5gB+*xwd1%x!!*e2W z8rth?I}@h|$%4AZ2X~njWhh>J2MKx_FP=?3zGj!5HSUk9m23uX)hxSWf(8c7pj%wY zF?CtRihn7Ok8jE&B7jdZeQjql{wj_`hm-j+RTt7{(d)#jqalU$!PS`2MJBG&@MqKc z8Iy%__Y1UkMqE6V*%2qwvTIwhy6qiQN7;8Ai~1#|)P?dYX1JR-m@UA2$>&i!F$F$e za~z3gtTmXaO!L2T^#xJ-13j0abGo?HIw?iNAw#3r;u70-eH5^zx+VPUEtfBl1 znO4yM)m+a**WWZ(*`V)4QH1nPm=o$JORcZQWbfveM@jw)X#$`^!xntb6BG)8v7l~2 z8}0MlT(H3mh-ZgpwNCbmH{)yGJ*Nb*?`LjNTs+G6(hv4)!EiCgV+Wcid*}#Bfvuuu zv}HSMnZ6!8mmoe!)7Bl?8Ecgv<}){k=(79QZVd(1zmW6IS|%}7L}qSaZY-IeSQ@c} zGyPoiqS;%r<9jAB>Kgpt!0wmQP$ztv%YO$p(K>&m{wMrJ!Q-2A!_((cVyboE$eBnd zox|z+2=?G^z0fqETNKwL(2}XDk#W<>>S8x;K1{xRpN1az51IsjnR`&!wwGa{2K;}3 zb87Y)Y~n^bJAmR1Xd8UAN;}kYc0bl zy443J^;OJiF2BtdqG3Q~5mvpOt$uUC2YF@jAs!IzU&$xv`bA$_njJR2E7(S7V%tQKMH0x;s@Mo9na_#F_a8yI7@<5ewQF}E;k}mo@QT6SkF+LX^?t7oYHeKVn!4I| zD(MXf2p-QgKzhys&HGFVt1B|QQ~{dzoZ4lRA4QmNNrL;oRU{S+Q-FhA#dqUCM&yg?pJ(QIj|vBc=D>G9+d-GyZ5 zkXN7sR{COCo}tA!sYSVJAU_W^U=r7^@KYvJT3V9JY3;(LcR45PqR;&(Wni zdk#HYa33B8j?wm|p+nlTERJ)S2GcuA>J02&Q`@#$-dwA>W7Pt;+}PvdVCz<3n!0BV z^?QxcKo%7C_@7NssTU#{s9LS-N(i2_YJ(db}&R-(^1p-I?w4e@{P5!{7zI6q)y7>Bsq{> zbjB*MEFnKc<=4||z%;y5qz9_(VW^OHK#X%3@G0Bct03-J>rDnE!G9_QT z+t2E+W7mrQ#Bu4$$MG!}ZJ>MW0$*iD%Zl&_z4v334JUSTSw73IKZDiLYUU&z0#UJc zqfRm1@-Phm-q`%=_pZ4Pn7Hy22k%zDLcQQUC+2#%#tY|zp`c8*5hoGnC%0q}4Shv> z7>f><<(%7_VBx%tW(NA^>?b>acq$2H1DjA$trPtZ{=}ARfr7oO-4dd;oad~xu7f2(EKxZXpF}5vsI1mQ zR)`?;iHSPX$A7Yt5@-sg2g%Rq=6~7?Gf&j-%4xYqEfyZXX~*AvbT&W2 z1N&(NC-pV7mkxkyV=c!>eO`6g#VBlMx^MIGIIo4P0`gJLPjriT2J~9rA5_~yFO;H0 zFG^xu*t+r%fA9>rAKCP=2F<(M@B;@bBImXU0h)D5WJ1N>2~&d}vK>$ffqbPBw^az8 zGcM|qdIQW6VDZc!&P$6@3Hh$!2SiG=Q+(EUEie2}UN&>Z;`RCsd*-WSI3u%-OmV+wt>8}BcJugRfKop== zph2&`UH1KEHFX@9eao&@q+amP|A?0s4?dXh0G1~D4^K>EA387sCupp+C;)Al3E!w^ ztF6ur$09#OQ7ZvL0u`{biYP;%0YGbOs+nxH4*Kj8>Sn~1jA?+eSgz9RE z^CBv2q|W(Rf8mNitK8cH1u$}xQDgBSVmgD{f4fs_f@?GIGJJ5=9 zs`|B}7A9}~pg>+%ca;H7g6^P#`9=)>J26dETQtY#wvx4b@;g1SDq z{xw@n!iGn0?Ob6905}iSxS<%fp5kz~XuK=yi&P58c*I{dt9u~%%D;Q9t8Q+lr+Rfm zs2R-CKhju8i0K#GpblR_(4bW{x(iJa+6rwkvs!Db>NX!EOjCuKK2vt)?CH%Kc}6Z5 z$*4)~2^OW1Lax~tE^?d+IJLvfb*5kIjC#jhpCmPp`?B3xKYl2ZF)_Y&X5)YSlbIE7 zi0JH~xM}(@uto$0`l}9VqxTt8L9(2+`7`^4Ox24Hpn`>C>)VGVn$J~st_~ok%DEr6 z-`hdhMF9)UMnN?doGvD*Czs);U9KSsh+X=ho_Z+gKG!Zidm0a&4Eb8Gf;l&S<0yX? zXu5naq;shs^p#x3vXyF_(sikIEzh`YBAn~sH&HA-gX(7h^pDGF=FqWJ?yJoDX0Ym> zhcOJBuy6Kd`LN5BW(Hnc$zc{34@S)n3>0+f53fgVHPQu}IqN^kKH-+nCTvJ-YFxQy zf8^KD5UtWrW3Q64i;F&J%VKpHY=DC>?;c zSN)a;c`YFd5yF_-Lgqw-?cvi~BRSGO7Q68s`14t-@&*mZP80W0vbSpuM{sv3D^maY zk32h6l|nw=K-*ymT zf8xUo()%|Dju$5X*4Ew1a0;TK_4kt1yW=Ondxv-@GA%up@T-s8%J*!-z!`5?T;wW9 zvBhMz$0MVMg!XQgcO00M*mSCC+!&3QGxW*zL)&XgC$)D?oRO2P;%-H`6@19{9B8LR zyD5U_rRL;C+Js|Ql_>4_&S5O2cRymY-+~RO{r_Yri@@4O$L$NxY<{Zi>dF0S=RziC zMkmSq@vs}vM?DeTWdHZJcUVF58>)DWb)V{UFFJQH)C)J1PxCigM0X0veOIyYEFHE) zBO3+}Q4d(~1tz?ttDx2$4#S)D9H5|HZUFey=bR-ZPdTe`+QHl30b+W<|4GfG( zGE#UpD56++Fe(}&MHHp=z1002w|F}=t*EsgK=|grPk5OZ(Hv*DPQb~RkwaK~VwKq# zhc25JdTxP!aDC(QhilF#0n6~?ukbx8Oky8%D^ve&JTD@H&3^D_3lZXd{+<$Cs8hud z%KfXohLLI*mw|&sLnLquc|QP3LK$b#R?Ex!12ZfK@j8;+*w+{NH%pB>ZJ>vV->P^-0Q5%de8u_ZDtUQkomw@CX!2J^8!zb8aQGMOy8$p zq|>o4@D&+>3l?6pA5eIWL}s9goesua48-hfeMCKz4fW85M}+c*MYBsFe=QC|5qa-{ zFiofdZCi&O_-C(bRy%L^P7PWpvebD}cxgK_61q0Oc5t{Nro!%%oZ6+Bm1uh}QUps> z)@?n*h1dUxQG@I*O0}=;o(f{jJ`<#W|2{9zfBKrKmw@ZP3>70arZ--3bvEV=eik6E zxE-fOnBIs|V1vYG^Bm5e&rlro7?@en``oPDKQ!u0@M{Ql4Etu6K_XD+r^IBZIOVrE zR+Qm@Ps-PKwwK6={cnAmFh}tppmT~`T z*oRj{(X>lpzk8)R!-*vPTsh|JR^83Q-D=uAE7@h+CZB|q*a(`h4&F;f>l6gHfnh5Ruq`Qit#6)C{pn)yc4yGwtDSp>a^CAnYBL5%HPzH}gkY|7UkS^bst& z!m#TvHLjr@;^P-GiKRo(R_vCX0D+G#@H%tYz?Xp$KrXUEz!0?j)Vrhr_{am%(64XY zi8;FlMQld@HZ;JJ-fTC|490IOpXM(xbwr@xlMj;v>34u5NW!=&5CXn~V zK&tt1=YDl%;%OBM8FA&K6tOI-61_XS^9{?ymW>B=@@;%RJsQu+ck@2uW#Fonv>F4J zKPaAaJl!73qtv1>%8FW#JJa}M>3Rb*Nm=YX%ROZ|l0-t~58f~1gv^!4)0f04_VNO` z-}IQyP3y7cx=SHk zK0KVPBDkH=C$qFu)c*P5M!lHnpSDtbRKiEaeKRur2kF)uKJ(1p3MRM*)RTT3x0_@u z*7J@tQO@gfduvFK@Af=r&a9kw^Iha#7^;o$CJo`FO9g*ru2uaLI|o7SLV7E8KR2dM z@aai@e>eDp1CG(>B-tHfm7L0;Vwfa&alD;HRLruw>w~W^26FEhh)5DDh9Ot0+&$0} z{}u5Xp(w0zP#j3VC|)uu9f>6Io9s-%vGEzcqlhe1o$3~l#jR$(%4*mI*BuVcsibLF z{keybeeRrVlkR6~J!)Wj_?)khA}ONp2@xwl6g!?4A?OMP#!8*ud_zXlahHKa%$)IQ z8YY>T-=x#aq1`=HEiK-)psy&nO{LfVJ2?M^U|s9x%)V1_yKBjV&Dj8a^lKQex^lyt;E@~6*>kH8nuo<+l0Pd66nA}2 zQV%hA$*xI}7Yo-rI;chVk0r_vg*8Q2rw{naNO|2;PY8Rz-z)|FZWmW5WbyL)oH6gz zTDP(7$`)N|m2(RDIhFZ#3!rtPfj`DyVaotf30{_ZxcK4GBiFUz~Ah&ke@iL!v zpS`JHDit-?>mVBg{EV->KIfew1`RIXca5?mwO{A;bz1#ebQ%mWt!oH@~g zAX*zW^A284l8t|X(8hw=)=TWJF~=k^t^)t}w15mL$H$T7B|Xt-=O9JJWvum7<{QI!1(doqa+#E-^(zsbCVdGsV6*Jz0!!#jr}yPd#HTR+(e?$A z?#CeqyfsxDKu08GI&;Z=Lq^89F22tBYFHgh)`qOwQi}!;lI~NbrSteS%cHpDeqX*7 znd>vH=+@`rFCZp{=tuJRA(OBc3;@_^Cp|Ea#Wbyg?jDQ&y4Wa!qBm^k@b=O=rI?Ki^K(;j#& zArXTOMF~~Qko(dYv6IMBJ${`vFiG4%jW1&r{XPA|$2+{_ru1bV6;e!Oz@i0?~{_>P(2~E zk`jKjiz_mqp_Y~3KBX9&f+>R;xVQf%!z??QLr>7qlyQb^V;mj~nHE5!r2HB3)mc(G zutHA5l|W_{Ow_VGJz9DW_|v-h%UEpSU?kEf#@XkFbM`KYcKB^gFFArsC}ep1j)SqW z#zmM5tgt=tIF(J0Oco>xBM$?mUKt^uP>d%$UUT^XdVVEg&~86?2=mZ}2LvK?Y!YL^ z!2$>j8$=Lm7?jLCPA22XuWpilK-3CD<-jlXedyo67AJ5!IeC@8D>0xGB+zrQbaM;z zn{D1;NDfr^+*0(83m(vOm{_4^sT^afYib*mq!nSaSdwNN1~6D25Qv&w#8OIK@N9!g zC@}}+ta}G^yF^DCd|+$H@dku23#j3bno(#_{Is5~wYn^bmokNMsssG|*o$HjRMU`j z^wKMj?Ji(-FA6FiJ>)JtGw&ggQK~p0tEbVqMmUd)76lXIPGQi@_)G#=0 zOw48Dk{KTh%0^Qaj-!-t4bxs5<)_5}|1APtrHeM0ZH$ET54jX^Da>1KxPke0XuqXp zDpGpbtx(uvMil%@6}-0e+g~=-ca8noE6pR~GmM4BDM`VHy)m_N!=ODOpZL|%3xBwf zabeWIFhaE-DRQwgY$51p@f>lsaW*7@Zxnq93o(!tK3?@H6S%J;Ys_4AG1BrA8wcD0 zyk?>zf`vBfJUxqcLjV{yo>5KhKCfM!oVmd^1RVPxvK1}Vf`nKScQ2kxgb+APVFOF5 z*5Z+iwaFKfs7HjiG@Rt=;e{~X7AqK^-B{RfX@i#bY^v3D!T0Rz9df<(fPa%ei2uz0 zECOW|h#0sFWe^b03`ocD1thzC-j#5Hf`jJ3OobVY^rKdV#fYi%)AaFCXGtt!J?|B% zlg(as>68B5i%chB_L9l{jFFN*L?=I0YS8gr@7Cv`j}pfJyYHpC!H&ut1@;s<6yPJq zFQ5|HUxii(`#;d&r0GA<-R0Y5HKtbP!uZ|lB|`b6qYH~z9MU(K3oSXsTrkF4H@3ch zVMq7wk6K8KVty=4>qyczjZlpJsnD`BoH++alA2%O@sES!prY-;!&s$+c zUs==<80hyCk9&;zKPtbS3DvhtbVvM+yRBg&PPzB)pYdW{eJ!1Y2zw~)8<^34bWs20 zk@w1{_zj*@{>WhMwl!E3@maA)H+Paj4qr2Q=JnM@D&z&&yLJ*84lTNG@|I_$#Tj3N z{ZGG-gT=E6m3n+J8;&c>>_PjX$Y>NbI+_39Pr9{+}9({|71k z|Hk1@W2Arw1R5F|zf;V&_MgY{5h=6z;opdNM4+xG8uY(jl<+^P|Nl2G`TzAE{12^* zniUWEI$Q2cvxy;~k#Jg`*RYyb3SzOC@D`a|^~5j{P4#O~y%}ZicpkH5{x0iGy($m( ze*Dt$6gIkW_5K^282Roq@05;8fyRK1+x;fvtMkzJ6W?_Gys5+`uK*5O zy{8uuFrmi|`Nv3Z?>!1|>+QP$m++hxp1C=PgM|32a{Y=!+@Fn&iB_EUUKn_f((g}b z4Im7AI@^PpSi3xz^pMNL_x^c* zuw8#pmMmeuqCB$efvt3<2S%gnxvNeUGFG2#FlY$e&?&BLG4Q$Rilm#n9bMwEv9g3m z$s|9l%S#y1W)<|6IlHdLu|^rfHM&nhsklDLws5abt*6m7^1X?%=efd*91Fo~^W3*_ zKykdfy<*Zxf&~BLO1FF~|EsgF42z@b+8iVV2<|}w1PM+OWP%RvPH+psb#Q`PaCg_> zg9I7e0>L2!1_|!L-F^Fc-tYVN+TDM9e%008Q{7Wtb>zNJo!;Kz5&rvE*hVQd1Q5g7 zHK@E#xtX`;znSS+u!}+6RYIDHH_}JnFEVt6A#Qijifc@5y00vNp=aUby}O*#Q1xC~~12(!ksm%bJ^%DBq92yQH?gnP}8tMiX5S4nJ1 zWVK`=tas@_{$|HZY^1F;^tzD&z*LzgQbZZ?g^D2f8|fdkhMs= z{c~H%vJ%Y@!Agkxa0M?f_xJxhpPT^+*e$TV6!|Mxhl@7v8<3s8I{d58=A4dh`T2!4 z=gBEG|I;UJ!?JZU_WIhXROo*)&Yd`rMN%F8`bU2F8WJ2G(ved6YP@|fG3mIa8MU)X zXSK+8JkNDPj`9PPc=KsyKi^!{cyxmBINoePJ zt^=QR>VH?%_(hXmucudhewHjlztqkcvLIukQ0;5CA25heE@Ys}cxyUK7hERA8adkH zzL7uW|LiQJxkK1jsEAS?_5Uca0lnSx6pj9{D?ilmy}-1qxLn^`2O?2Av}j2=s`oJ| z7YbgU_7Us8prcTjs2N`ha_^jRs!qbSSNqyz4B$Axw2ke!$l&SbdhB+NjepqFJFX;; zUiPsxrz;^LESYNY+c!Ij*HTSvzPnPgp+N?=s{W@YfYNRgdb)aDMhX$g|95-Qn`EM# ztkDzWauzU$Qp(+0=!(DnBC^!zoD-jvPDjnT6DL^XML`Q4DTh&qrGj|P_^bgc18IJQ zfM*8z38ER+S$#gdz~z3UA(_2oxB8>kT@Lh{QJ*h&?o(VNoxAj>iR$W%M`z7ko@Lxh z4%AOYew8^b4CC&-CpKC*W6*Qki^wvX64a6D+@R592-DUI^A&U_5GextSS^+4B9u#!wuX^Z+;C zVIKc;uvMKiFPy-C;|CGs@~IJ1v74M`N6A{0x_9zUm$?5C-B-NKe_KA3_lUjcLai-y zdo{dCWniJs3FWr{yB%b#PMv{C#4jG6E)NAciVu)L*Uj$J#pcC|cg0C;@oYK-45nHs z70N>$M20$^YxwO2wH<;UW2bR&BSR+q1fj1MUNUOBg=&cRd+l3S{iX`(#p!JA{Wp)R zi2_?WUYC5%hs|8r^4HWTH>g@=ab~k{?|LXq+cKJ`uBEi#@lWkQ&3mDPWOR^7TXR;@ zyywWL7a84k*Q0i!=o6p}R`0s7h&DNUJvv5E?P@@UZ%TZ;0d{&Z??{5!%@|ozNI)5W zDB8g8eo{2o!`1teN=g$P*jN8SvuQYfLsZRXiTEllFa-cC3xf@&X8LXT-OdihhctOBmiXDvEZC%4B&1piEjWa{Z>V0leJW4c zxdrpC&Abmm9U_N9Y>B?JZTwJafhnwQYpsq#d z_D_soXfCx$qafrQe^h@!E8HZDWiTX`t`@_7`q6e}}wFdqF-cftoJUJHO1 z8EO6XqIHicEQ_g~?A7;gw%dPE2rKs1=viJ2qk4GVqfsY>y!pyAxikf2*cY3Wu^z~g z;Q&im<6Z82UzCQs4vGNEtt5j6gS&=gYAG9`Mh%;S&V;xN*UYLp2vmUliCgFeRrz^s z(|+HGboeT<)_8PfeG!z9Z1|@ftT^uKk*=v*Yj`P;Me)V@mDVJ*QhM0tb^O%GD_Q}~ zSA1V+9RAdj*L9fqSloCzP0pd`+2@C!sl`Ale?=bew&J6xye7hDcvJU~T5rMa!4S1H zB&F;t$6Pj`o@La_aGz8B&--Gp843>JnI176>~qa-=a=X+@D?&y?<*^aLFntPM%QOy z8lTP426BJ>i@D6EKkwl^|3@*jcD?v{nd7Y{2sbw_}JrB7EdE4d3G4b2W{HhNA}C<&9c{HwT2F;=mG|-moC$V$(^`0)4tz zmSNIYuN>N+f&Y$)L89@@e9(mBwncCnD^LQwn_o|;2nvcwn)Bx?Vv}6vMG%Or*iuWT z=cfY}Tmo#QLD0^_4CZq&H<-k<^m{snk+%$Q6`}_w{NGTbR<(}_VRXox3yL(> zrIL23`p|M>X@(1xR_bv_sbzae4X%0SIJA#!ry-P=HHnt^$Xws%svO*)>5nrUPm8y+Q`;@)WT>m^U0%h9 zr4crB7w-zCe%}7OetdgX`ZW{^TQ2tD|-GQ*ZsyF|=^Oaarx9cE1pX9WhW_Eh1~8LhCUl&-hs#a1)p zqIW5-lU07pYWm(sj%FUK5f~O`p&xqIl&T#ws40ew;MOk7YAwmbz}|f{csJ7a@rkIu z-|P_ts@F;9;9{8^QiceNJ2-2t)%fE6aj(TXivyxKzDu+szMl*6o)u6OxO$cu4)8Q^d)4D(%ckn(yEH84;@!;MaI7DDX|%fh`aJuGnYf z$%qb-HTY&~YE;c0f=$_bTAn8T@g?76aA1Q#U9QCXfgy@vnAO@8T#F`V?DKOzfBPh3 z*g&iwBk@5I``)X>Mn_jaIr8`~zB-EL2L-x&|Gdh$JK%m)7VrDw~UY z+KV1(N6GMw@BCG|PMj|5Kan8#+y-8;H}Q1`5W|l&>j?o<1y2Q?Au??fZY6UZ73k#uRDrpQYK}^Rs7>X?QL+#J0hOCnF|xd;ok!$!`rbHcy>K`tWhG;M@}_VRkR>HyC}&H zad@LvEKZRW<5xFh9;)kBI+nJK6uY_3YT_3tBF8incl{ZzewD6j#%0@`J6^+_fu%1N zgG4$6R6gpQUT;*n+=7nWEDQ3qM;{=Xl|qk&8T2;J30H-f{Mg@B&uT)RQjXX=(F+*s ztNgv)t&b`$4ILIu_lKe*4TukeL_ifjDR?K&3&9K4S$4BFPRFOrN)b6EH|E~Z8%aE# z{AT1p8uUP#p}aD#|7!ftk^is8Cw_i@_G~m%Q9M-9$M(`nJ=oJXTi^!hf!^TXLg@b< zPs%S?4iW(_z>~lLp8B8rhrPG_@5c5k)c<-C*JuB^Tyzif-yWEPriuUg6)>`+|J?va z>ferDTFbv#(MwE75KEvB7CSjVx814t);O4hzre+{sL_soMk9OL6@YSba$@OuTRl}I zPYMP%PFLz>W@d7cMWI!vBqeQZZkm~!8@9StR#a3-s)czQmiFNj8{^>MuxE!rBVaHa zZEbCD@B3fb0($)HM#+hZgvoP1-xG%zgY6f(99_2djQSTBeID;`*np6CYYPZ#y|2IL z<%zJ7n2-V3GT!d)?%eDw+-`ols|y5*OG#<4o9B&}nVi6o&cK-ZP(V^&b)0TyYC1VN zso&w*Lky>+Ah}Tk>eB5`-0Z zhBsLt&Cky@GW2Us%~e%Z`T6Yg zZ&_H%8ya}o*$>Wl5Yh0|v8yoxchpgW*f!?erAMdLQc|~cd<<;S$R+auu0g&X@E+CsQK0Z!~ z(?2nx=J6?zrb@rr>Gpi*ok^c}uEq_v2v1Dui%M2+v>-I>41U-0jt*ZXrC&?!UPvH< z@SlT&R>0p3dXC%S`{0?$Yd11Fn#yVPiky6IBK+aCOai^E+O4~x;Zh2_-iFb^V(ZoQ z$vQ@$Wq9F(9_sVwNArzib8{_5oxa@Uii0kq{Hl225~&;pLxY1DI5>|J&cRz_xmVlc z>kG}!TGfW5LqjZpu(Gb7d^t7Vc4^7U$%%kA4B7E+7KBt(UU9iN_BkE1pSvLy4H4J83$(jOB|C6FSPKTr)H7+}QgkEOPF zOQv3|kgwMH<&9b0EWK>4c0~Zna~#SKc%ZKJlk)O%^uRa{{CN5@;Ek7*ywQ92TK06k z(qX{wS-GQx0gzl~&3DjMcCpBvhp8FJ@CNMqUtemLXG6Q26;h zB_t3)p-0lOui(~G5kEc^78kcPH935s43~Hh5UI~`U&YZ6uAgvoe`L`IFjS(%6M?I- zx}vEE(zk4A9N|K~ymSwfxj@wB_!c;wR{ZcsLxAv?nZDc-HE(`9bfK@w+*18_8GedxSNmm<-ni`hpWZ z4;|gQ*u)_ZA=MwUuTRDAZtCui6xoZ}o?u57yXrNzaJY;0I!O!F^+XDbX6 z4l(xh^c+rMj|>k_=dw^TH#avkGgDVx)7RTtY8Pa@g(E&(K9V7P%_k!c!80TJH@Gug zrs5kzYlFiREYa;?`wW$OA=3%Wj*C)ao0%~$ZpWVGd(IRsGZIe2iZui_3j{U4l(Mu~ zu1+B($AF(BQPmwO;zwRG#GTLig1s2Atde#yJTl+~)G_m4E1!tfy|4s2tbpNo6s1aq7;pM=aV*=)`oKa1@P zflS<8MGfXs!Pjfm1^D}vkA<$KO14HM4;l>H)vF*Jhch_Re%0hWJ}PhMuGdCVa(N5e zQr@yZA2ky8Dh=vb+1&gZsY`~6Cl+NUAV~AaUBnATZl@=yDXfy&!#4J~{fuQsA#!A6 zd`@Ax8Q#A$i`0MGdrI2N!|sXY;%;5pPo%X08TjCJ;lVKInz3Qxy;Yv)d~{h%d3`zF zTj(6ivSIHaj*is#_DcU1*h56XXE1WI5+cdiNuuu3s-)BM#ao%RVa0WNkcz_1jEF#L zea-hE;g*!|b6X@sjynf0V~=-V_jrty_F;PI%E097cU?Rbi~?GDk114d-n^lr$_9)S zDW~B8go7zFY>N0@vgh{id}3BjEGm9(Vd0K4ZL7+x+YjmM!~in4``LI7iz3g*{jBOd zl^Bn5F=gW-)7||bYOAMDS7@PO?L5(){!1PBi)V`)oNVYouz(D#fs>@^V;+p^=?XRu zUs{rn(joM^vEyna`WhVU*sQN1_L=PO`uaC9k!PDCb+)gEB^0ex7pXKNR)2T>Aa;y= z^kgp(n6>WoIv#@sBG;_Ri=sR=adEx8yliZ2b`qW5!{P9oo13v*NT>JB zX}j0eAKS~d-bjThU>U+z5-OV#VxW+byTA0N{Zu$fr(PPq= z!*x7`Jdb~~Cm9U%Me_@3{x;Nwi~NzS*w zM;_}9{GSVmu|Xp5wgtq_Bv#T?>u}KpQ|z)C!!qAFZFGNg=)$U=cJXJG;g#R90Qa)7 zvs-cN0@~u9$_y5`Cge>Y{33Enod&d9q$_xdNCfwxB2jw-eovh0y4oYjaA(nYq1D(ZU4TI_PN zE}Tz3#Gr>$2GW#%EQiBgJw0!GBT0Kd*J!hKd^!2->DdB@^CZh9FU7`L32Fx0_2e0k zsnXgx{~ZaBjy_muKHEW*1O*26MZH{KTbpQghmw+}3i|+#6vE3|XH&1=-xxnsz#P0A6=*b05@e2ti(OC{iYs0YjZN68Mi1LaMZb{$ z6(S9B#hv$KVZZp(kbZuhyOtC7kvea~C!74qWzrp&GYbg_ii7a{uhd0fKUFW5al#l@ z27AwE5O=lQb6>E=JBZs4M$Y8Whj5_6+@F;pVOCdZ6OFgu{qD10zXPSNedhR5@yfRS zVJWCSI#cMf@(#|)wkWhG(NX$1Nibu*ZmQ;Ja{h(EqeCM< zeHMDrlR69H_PO70Z(hFq3W{)EbI%^GjQ6)lttPs4o5^c*qS{jV?ylAy1V;UmNPY*~ zI=K24t8?mmn|#2Zo0e zmA8hHSup~SFD}|0*F*L{NlHG;9Rq?5IboXVHohnsK)E@5G|)BjA^zBlC2?|ILFqj`EmAKI;gL3;s!YxF$0oK zsO_gT?C^d`Lc-`*{bOosYHUpTF$g|{Kb(s4%aUc-^{r;VRUJF$MfY2r?1 zwx;{}6n5f;szfnO=;mp$gz2ep7K2ajs6eVNF;6YZ-XVKO%-O*7Y&gZx-LP7^;h_Kk_?$Y&Wson4CvC)2UuER$F)HOct z_#^Ncr(xTlM(A?A6+$K<>6xb5=ZHBp@x^|%-p}u36Wsn4kpIxo5a43Bdz?uz5y@Sp zfe0)+`L6%0qGLq&7~^t2%V0<%)pn}Rq_#k^}fxA0QxJO>4r^uGwVob&is zeLHM}*ov5-jrk}DC)*#sjlpUw>gtYF1sw5i)~%W3+%PSUJm)lkz0uJ#q!XHxkqsL< zW2K*vz{dlLtddz{M4qt}LDk}nvb1{N|5ZT(seELoM*>D8uc=rwBe^B}Hkwh-U;;dV zHa|H!G9r^;d)zBAVX>Kc_nU@D1LGZ9W&nx^2*gBA+44b|i3H!&>@?(n+3yT#V6nMq zEbmzLavW?wJq>}MIDvQ?!Ke~3*>9P5e|l>EnELt9dX>A$b9R{PVv}=z1ca$&>Lr&) z%SM+IebE%bV#a5mQ&2E8YOz2|Lp}}tt>1i~WFT{4weDlDI$ICn_ll~EgxziXU<-5J z4==(DjbuDX1%?8$lM!BDdl5a2%ke(Krr+<)!d2!BC7?OUaY7f|j(OAiqAilDEv?Ro zV#W2oXf9q}M%h}uMtgt-27v(E4f3z7tV~JK1mdYrpYmM9(l-QSp`-GrhE{VBSbiL z?dPUoO*LI?onlBs2Q`T^W{-Dm{%bO{y@K0hnePa7bNE3mo!l5OSfGc?z8>eCO9wFv zAFceYSN0~aOKi>^D{gyeRCTm_;P1w}aw2I!+&-C_E}*dT1i42?AdsH@F_;K=fp5Uqub!a= z1+0I_U2aq4FjahX&M|y0Pm-dCfizkg#LOhe&f<4gF87-C{XemCscRzn3^&~LFMUO3 zyG3@0RqUY6w4~*RVwlH5#gz+rG-D>0>+WRPl9_`bJ$)fxJg{4LrJzYq+Pm93sVBn@957^2UY(34l6iUXCQ2EX&9umzqMR8cvT-C`LPjBi!zKa z4%!ZBxc%OiR`Tfk466q{@OnKK;_dAXEdO9J+7M$rF(CBP0iox2baceVD%;vtbar;0 zhb6pMo#d6vFubbZWM&TU{p{i81@j%Mvls&)lj6UwUltYOs!aNd zA~Fw?b4viH3o0^79!igdq~u}b;U8$?A6LpP0#}LlN;7y zE0;BeJ(iV<(W<>}c*C^4o*kOYo-97ZsL(qnQd}{rV*_U6uXxEOOl!`Lxg1OM*k>5%jD%wN)6U59oDVr+@-~%Tt>RgAo7}XkS zW>JW$Wg61VE9Ez;QMaBM9sJHEZ5&)*zs|Z&w}g2F%~3eIuo-xhUJ_=>ml~-YWo8y$ zpBe>cn?G0LZAtwSD?J1k^S5c?$XXnelb)}bIsXJT@B5*bY~28WSOBC!KuEZ_xY(>; z<$|9^gTB*Q3z116itN?rm|0vDdiPHCcAz!ElDGlebUR~;iXOqPqQ zAcOryh?V1y%-WyR>f(M~xT8v6dP#|;_Nh*#OOHI3sgyS^k>lDz-zZyRyHRsl_=hyT zK6>2wInqdwH^F!t5(XZGX>D`r5xIWG2sKJeK6gG2AbtI5;-^kwpefkGj-8b?vVWcL z!-urAG$IlbIo!`=ZYOJ!|L6oJbtWPplA`R#rTp+a3I=$FD<{9YjJP#JUkO!ndb%F0 z03{uZ=s|u^9lV!KPJ=njQeOspxK!mE7Rxj(PSV-<2s?_-c6MxMj&SFRB#PN7dQg+078sGhsx0ii25Y7e0^2Ut z>Wuem*4+fVC(@A~HOE!z1u!i_faw9?Hz7a;0#J;a zx;hXz0tm+k%9#5DT;W_JLqiTc^r)z)7MGn`(?J3NA4QZX!@MeQt(M^~L7D>#2C}sG zsi`4_NgjX;@LGFn|g;a ze?%+rG5J08;~yc_I`UVjb5*n_bUVb#YHrdwx5hgeaD#ig1{WMV!LomKm6nbPPtNzx z=~Bl9(HIcx$cUpiPqiN|?RmZ`#tyP;03o8ye1qK!{Y$;ojSV6^Jcp;phh^Wp${Iba zrKP2wP+VmtC1KzDYb~WEEkOCw-F3?W;I1H=7~WSI`UJ%s^O3X@IO2-)H%zM=|*}0qc!UDk|>LKv(y6cM6u3mDSxXHmd~2>H!RPMP(&B z8=I}|?%^nUaCfuwmT$*L%gY$qEO(ohqx`^P;5#35mb`Viw(r6z8~OKOkbqe0Hp%)= zfWAGa8q3}IJDLfAkQXN>089&LF{q(o&OHnZtp@;`2M2d%>nxt`mY;N4;*A3Ck3>x^ zEP&iGxhp04i=$<~%l+Bqnt41=4~SYidR+H0{}4j3$_=t zD;5~>Pk0~K!&-hHDfkkwBxl6Mb(y|jTv!kj6U!0y@dTzn2qcQ91mMxXevyT2ZftxF z4MhP>-na!kk=<<30MM_BN?cuC-FYWo_rZMQ@YDXo)$B&05zly=bEiu7srR&3xEwk10#0|0oXD0IIWZbm~>AC;i8ia@iG8fjfPD=35?M5=g&6-A|oS@ z7McMLM9t8!>Xwms!|2&Rs6V&XwP2yqN8}|x)TB`?2g)bRPQpKHfYpdlH$$gQ9K4^{ z5uJQSTTxbqkB(|LSO1QStFE?o0)a?`wrFT*1P2E<|0_USS6A0k@)m;q4_PrXJ>5`* z_?NvI=uB-{wvPRW$QihLc3>y*A4=wbb2|n||L{S0X1`nhn{N6aa>)ztKitllAJ&Wf WW+;iW6Yx3oY2LQmQ-dfH|`xMwO)YJs(d=Pc6kq@i?>1~}bk*r3EAx!rYwD>^K{s+T;s_wl$sZdU0U z@S+UmVfT>KmdPy3K0QmnRKG{E}9S3E3^sT!Kc>u-HgWa9IP49$>uKC#Y`A( z7icd_!8)03kJ^fv`&_6699`;Ry7`QL>u&84c^}QYUW7#Wi-~dfp@HI=$4@Y`akVSI zh=UmLQ{T{@pz+ah3h71D7tbwVc>Y=O3mAYT?2$_^B|SiPuE1fflnf<@8IXT*Vv)u$ zw7su) z4H$Abv$PB{g_eA4AIEGiuQwb*Zd?scNbr0&dlwCX&)j~3qlD{5j4^FfwF5DlLdzm} z{l5UHu*oeba3Z(^pTJ@QHJ}^9pSd*T7KhI9nqH*togrwPs+sy4W#;^-uZ7{2j{S-f zVAK=c?52HrQ~fMfe1vLNs6I{m7winyJcLYeI`Q&J@_rC(psdHka!=v2pG=gyr&|o|IlPI1JKVfpCf1UY#;jiHwqPC-c(Z?>`MB>TM^=)uh-=`$ zKy7f!1qG0u$Cyc}!EAh3+h@XonPU%`b2iA7&2TyU<*(Pj6r6m*n(naH;dkss8n0~W zUNXb7;F?2RvNg+b zi1v!*b6_E%41eel%B46;Y9@4P27c|5+NI#4{kC+ga4F)F7HG%E7l$-|(Wgtwh6zDx zQ-V*0*K|HrkGJ`Ll5VVM7+( zbc?iS@6wJGRy&6J-JX`(zcM_Yk}fYeO*?KajSIRc*kF{3*|;jrjx+^v*?cPLk6F_S z1yO5qw}h*HJTgH%UEO%c6#kRQ#p7gpswZI-BLVYclpw5{1bx-jaEY3(il$*Pyj`v1 zt8}g-{z@lkBg#YM6*y|p##m)t#k38sgzaH3Ja(7*RIcljQba<)l4Qkoa?x%SwkZ@) zRl`tjHszvjl{}P9_7M$$x=v4RCVyH}V&@pUYrO13R#{4qN(GZKKLX+7%tp^xW_Gfv zsgg)ma!oyAgLkPfg8??06_}Uj!>FA_9aJ#Ce*S&-GVyTQe%6ceeh?8WhvEpknJ|#c zxv)&A?|o}eT&}&jh;Nv!9@BIi{X+(;!OV;%CPeN%O-~4_`uQ-9-N_cYF$U&XPsdmx zkgb+82pqN_)1WZa2qwE$Wiw}^maPIi{G!&)fk#07;o*BBC?3aJXmN<)ld)RI`aFtq z>j5V3>S(6(#*od7sw0e#%Mecw>jh%s*0!F7@~qpFjF99hL4%4SgAa4F8I>`vA06E3 zJ^#D?fj;w?ob)mI~lklVfj}&-q#k%ZHOJlfX62M4KCkeO*L&S@MM4 z#IR~Pyy6M1w9?2CYGkHy0@SjnZ)Ygf2LSjbqO;hjgSaX7Khzx<^TQ-#9}>Tp5~W+8 zZ%l|!-F)Ze6I{-otqR3|R;&ps%%N?7kwG_Nz<~c2>>QVJ(ed4s0|02R{!z0Qb3FJN z;E4*HIy#fD{=%=&+*3W1O)37IC6vo?bkHmiXCO9=%?UdY(3e||a0(}cH1Z|+YT~d;Hd5!ePv}O8wN9uo$)Nc z20B1?W9p6?!sSB7Ys;i223iQ5Kh4qXQR-1QdqEjh%gU*}*rx`b?eth#r7da+_PADF z|I_cj5m7d1mv@@BJfF3Ym;}@@IbAbW0~USWm+iYtT`HIB6>-z=aWyO*hjej)(eLgw zK;F0Lg3cgO>(2pKK|xeZJ1x2j8ELPSG7m*_NDKn8&+hwvDm)ovEq`n1*N_W`AUEq& zbG=9507;>_!Y%CPc2*l^^zJ;d0e{g*obct&bhY(6Ar`&2y_U0Z`p)!vv*7bAc z>THCQU!qiORZ)clWDQ#TiHdP0=L&$GU3V4cnblsLw!?XCgS77mHDkF|S)9ZbZL>sk zKY0OkYz9^Y$osGJug77bpeSl{#tI5<%alztR9>z>*H+QL zELoc3FJ9l4-seG37Nk5ytj*VGz3)D_!NA(KW6)KzFPUGbte#}EXRu ztR@U&;ykZB^m|+nZj1fha8+xXJB8AyRT-9yc>Al(4!B4z0&<~0fzey-tp@P2SD!Yy zx!RDH$Ct{GcbRGXk#7Ema$?n7H(&T?FNf8~mj zTNbM(qbCf7#Ru~(zv42h#{WClv>AB(v_t>ElD3NUX=T&gm>j##`9V2?Q$(Tt`_T~) z-fKMUsu(N2MzQHxTa7*?tB}TO(KOn6-euc24t`XxGy)kkt zyE^0Xhqe`DXoQAP#FKa@Nr@vC2AHGr5v4`lAve?cnO>;09}Uc`(CDpFWEwv!Pv<)l z0Op3_XQh#Yti0bJE5{OKOBEE>L)w?1H)q;A`*;){MrfG7AMw}?ABE+9 zaS9!x)5usE%_ljomQ17bYAaO{dFKXaJv8KAq+z~FuqXfg^`}SZs~72~t5ffI&EJIK zmr8=`r#B8ET6$Ii|F-UX1*h@`r~c~(`nQ3d&^JO$_rmM^_ zh#`Bd+h0v$LkGHOe42z#MS0gE=)MRMZ)mh@fjv$r5-8g9Vl76|l@{+eot1ugq~(=0;7g&EcYE)DzeAU`*wnKxpdeGU8h z^+xoCWBAF~f7L%VBu&%$ z{A}B0w%VYT_X9eoP2_PpmCMxl_9k_2m#c}xW*oLOZ}scV>b6G#O*&r5+v&E=7N2LC za+`{jgyWAa+vm!(>dJiokw38xnDIE*T1lR(OavF=!{df*<-_|ZClmxlV}Dbfv~BMU%TVntVoy;!|e&0o>MQ}h^wgg zhbs76M+Di@Gi`2~scGPSKlVQ3F^r4~ksnQQoy|rE%42&D)wfuPd&p$U?wznP9DEP! zk@1W5i^hWadYkC6W3UDJTGDZ&)rpZNZ;AY$h(5NaBevg#42Ix8I18Y{nQ33$-tb5% z7rKy;9h@ncap&o1GdLTc4MCHwQfQvY6SkzhzFwHbMnR~Cn)jhz_cU>}kt;cw(;k;6 z2j_6S`->XTI)?hk;--B#8nGugTej|2rzHWUA|R+~h_pz{vX2_BG2v3sygpD_H161C z!?og*A9$>);&CmDY^ipx?aEn#>Pyx3_>l{m1Zp>jlb1^xjxaCt-SailC{RzlUVnAd zIiBQA6Oax3;a8+Z+h6+9tjt`%?R*}3Ss0K~P4}!XUQtwxH>TE~_*~1gEzDanhs(w# zO$!0YP}rDkV&gFcInKP8jVwUOG&aebJX93lnrFt4mA2SvK1cj?J*wh6R)s}qYm!S# z9yfSyECNpY$IvcaFn>zaGfs78`DHepc}GKXG5x3&CXpHAH@|8jzHrN2aNQ8k2aL8Z zFIZ;f(G})3D0SK=P*CTG0Hh|y%sU(^NN*86rk>6iMjV7H6TtU47saGso}1GA?(nvq z<%e4)sXwlj7^Kje>*5E!e;t-em=vDP51Wx`>*Ar)xK|viHU8wI6FE)MaRx5eU`z}M z^@r%&7!k0+FWKI7yg;QT;s`49QE*9o=lvCOw|(k@Dm0T;cV+ZtHf5M8eu+E_kbeYk zDyF31b42U!cO=Dn$eiJ~chGGB1&EkulO~=fn3}wQCekn{ ztsVMVx6cji^$UJ<|Nde;%EW-cA80|B+CaO^w9&U646{?^SYdmxKo+&J7`fhiug{iR zZ|giMx-s|qeDkH#E|#+I-}sZ3`v;oPMLyF3!Xh<|4wpIWj9)h$_19m`RoU8lbh~DV z;QhcEA8oGT^Y8u&mLH~R3Mu5KkChiyc{?n&&4$@0>Qp;+BC!g_ z$24S?3$rA&r6Y&OiP(K&1b#8$XvCam)4h*e&&heqp%$qgTwJNsn>YgQ-_~)^0AmH` zPNksY01ZVC<)UAoj9_$KbT8OS9}|sMrQf+`7fV7Yb>9=cjASk_OQwuJs2i(1u197X;9_2V%{8bTZ z*c)aT?2I^}fpSRv_Gq^QjRcPR@N}*@a>G|1WQ2dRE?R@3%zvk4D~YyuB~Ihp!ToTb zDoavD&6&Fv(=^r6s899^IuHq&p0G8N7OL@(IVS=U_|dwLn!a=YI5Ey_E~pQl{l(Ez zXlk?(i%Tm(F);8^E)E1flfur}h^8IDoAs}xE*Ib#M}nKiqkCNxwto4H>j(f8Bax>J zwfrdi%7kLt7@6T*N{NM|dRYRk{$My;TvkNOG~?Hx(Zt6=o)&OxRaCd0o&8#2#HfR{ zz`l_9<4vODYy52dB>gg1Ch(mCzYLp&nW3M;xg;a4hpNT8XfuY+xgZ0-HI{>g)y}YJ zIzQ^!{c5&{JyE=_|QKA1rU*(U>9Qf*qAKF!<%QK zAF!Gv->WS?{H=IV0{)fymzR8sBkk*s#-%gMh$AjUFuBZSu78R@!Vy~)P}^NKO2u6{ zPhoYZD`CF$d8V)5bF#s2@(0;{-0nv2sLduWZagbf)jH1A-X5k-{$%v78nw4@p}e^0 zKp(rKRZ;wMC?qy3V}@nVl@!)bNeJt0bo{*=N*2{9_UuS=Xi!?+x2?TfHAPNR%^q{?^N-8es#Ap1 zlmRo2naL!mSQ#%eiHcu_2&s_U!VAnb>zZKlF_Cl<^T>0T{A!-Rz71K^Z8r`tV%i(8 zuI#(*1f@2-$7i$tMyG&hak!>FRY~olO+qk`CK7_1cPBQ@B^e3@#9CYEd z7~6N>?3gorq^6A_?WnmwZGS6KOzJ&8wK%6_z1B-3rV3c@TyEnxXLAx8cY;aQDsB>{ z+gdq(Z|j&(uc;`hrgj-J_|m)Mt}loS)xiHN3e}*Ybd=UIBWQy)gPbV(lgo4#TSdA? z+|fs@VUxb1#1$*3r-to3fw)SV@YsCn5>Z~bV6hgg)(uwlxL}gJFTVx=u?0C^)@s^T zj>928YZvsP72=)aowvU%yJ*~!Z!e5JkRdfjy6%Kd%K|bo?W(^ed}d!l?|}q-fW&*H zG`B5g$Ehn(E*8WF>@^9-2IhE)Iib9NsAdLu%KMj(sagW>)BmvJ5lE?q|A8W|2_XXn zj2(salIP%vWh!%+F+Tz>y7U(<7m#pBeAN*t@+c+yqQ*m=AUzqmXfcM&Lgwqyb`@(1 zK;;`26+dm+^dQEfinrvwieLkP>#P~w6*S|wPP0E+pUZvx9`ZD660^(6d8Oo)e|=`- zRRL>bEob-_{_WFXyP#2USmw9rgDyUCa+Jo#_C@`4F9jVM8{92CKT+8F*kAbTP(^bb zgF%1L<%;RUoF_Yt*lq_4b7jqk!fQ%>h(o3O6e)?-BRJT`Zw?P9^Qkn2Af&SQ)W>+Z zMI~iaQHXvatNnO5cCJ%whLTi!(B8T=Ti$cvh~`%Tu^HBQ)342x5feQ)hh_Z72i{%< z!;!qwcB=!)=`tK+b?cZYpgiV|fsokrB627&0)XiD>e4l>FA~yi5M533q?rcH+!iV3h+LG#qKA2XQFV%v;L49B*BNtEO@C&OW*<~bF-F?fb_Gbf(-5ARb-w}m8ChHBp^ zjFATAJNe=uiN2?E29`F^54J1|{bXt3M5eT#Ok0J!bd!RUj++{=3)O`Rd^~O|RBA2X ztk1O`A}eql5{2AWHHG^NS^_2!cMH0Rr9Lo!fJ}BGDs#dWQrllAb}AQlIu_W$!UB9q zQda>{K>Kz#oz(?(_AEU!o2q1oRWi0J&+~+0hpc zrzg%9)&^h%MIERIYj|nTVkUFS8OrX=Ip+ma+(zF&yQm;c{i!?qn2w}Rg}faBvjAOmh<**@x?>WcH39HgTV-Reh}!T z_=^!R0BW1{Hr4@-QkSn)tR-4H*=^J7S;B`9E+UxBd#s#w+B*z@xEu#Z#gPgiA3_ji z22JvHLX~07nBo{C_g23*TBqAi^tZv>7KP6q#)WS4*~+*gujvr4*!5=)QcVzSO4_x%&e{i9|xcc z9vvuswMJdZ6E>7SErpkwJ3i0DO1guMiFmMZR{MaPrKkQw5_E&4_+rJd!l4c{u=qGN zY8S>5j8r4?tf)`dJHyd={oRypUi?SA)CX)q5dW09oTZ(*{8?BY5qZbV%*HI(jX4J- zy%}W3)DNlNEIBeQvvth78avg!1bY>m!<9|UyBT2U@@*4y#y0KbZL&Ge^m8j)pxf;3 zSqQ?juYItlg2xM0wMWFIoX7N?gk`Zz)Q%l>2h7xQhtKUTS5av9SEF2K`00b+&F2F% zXtEGl@ZEkQyP;w0G^pkj$##5Q3vksca&Pfkddt0oWb?A?Djpa3#bXECbvDBCJwW_C zh6a}8S^cDB@BzlZkbq#>s_Dr4gWz7eNUYsv*3jbMIe{G~8R|xNUKE=_B&t+*%Mf=y zGqz&D_2_#g0^ZE^x;IhgA)q4`>1tGn$1R2X#BiJnN>^8xv$FBBxFJQR_3?u5-J4+i zrrhkKY1s?>k4nxT>F1w2k`lbjq?7u~(N(7j?|48p==a>)ARJBHC!IO)(SOoxW>$*szGPEgfxFTTbf06E*2e^;8+SP5#B zPF>91iB{(Y%KSnBE`&-@m@B6}OfO$4q>nw6zYOXJQ4zc&OdY?7v7sV;a4@GsZLq^Z ztvI%3fOKmY!EegiXHsjX$$J$Y>gT?V-7uCCR zk@#7uqg@JAXGB=O=uAl2E=$ZgrX$_?072o69RZ%5<@kTIf+^FCg$EU+MMBOmKh)g9 z-EA8bZgd`i^U@H$c@uwS0X|)xn!~^ShTPr`i_XQu;65C-Vu8!oJ34L`TRG3uwbc!S zjk;E%o_nWveyPqdFV2i|h3B1&!A5!(Z&y`U*P=te`8Av&yc?BF&B}E1LI@TP@ULz* zOPXWpWm-t5Vl3m~!veuG&!WQ-1L?+@7lAA3H*)#!=9a^HX#Y)xQQ~biJ>}U^rhgB) zDVeX{;%lNTTaAE~=w#b71(E1Co+)IcO_f_M1E{7@V)_A`cGubS=+74Baek6tc?X=h zAxztp*^-UbA9(t#hfR9@mHsB=trOgqH-?KmudNEtjXTVxEXxQVWNXtX=K8CoN=VWf z95ZFkjL^|x5&GH+?W7_8IK^^*P^uy4TVE(Vil~;SITo%Az z(M9T-n6rE4y1L(BQr6tTJV@wJekekt;pCtRMRd&8!SZqqoel%z!i>BR#lj zBer<%+(dibO|=J2?iW{Kt`c7PB`2As22oxx=bm?6^pqYV+i2a_^H#UETN}&wUBXY` z34%=CzRlDAxM7s*TAG^9v79YG_>>NbMR5s&;bB9QFrr z;6&FV)j34lWk=W5{wDN(xlww_o=-^e?&M;CFx8pPjX1aJD(>#YOlEZ6q5He;RER=k zmD|tT;zhlNyRV`urxC4Mh8RGeWv=pIhdBuA=?&Y}XzPSR)Yv8?gWZW^1BEPFrl)}k>h0_wLYM@apNhLWS&X36Y!wD0`n5`q zc5Al^VrFDZozdzqZUY!^Th;mK?PKm>pZU2242QB-XtvmGq;%Z<^}XN3A?Dh*m#*r7sw+%c*ziW&6wf0QzstGPn??(sCX6B@0|Rsq-82OSzj5ue#Lc@gLXf#6HO_S!`V#5o zUf>rffH3=e_~fZZbGMOBO_flQ5j>hIHWr8TB<5ZfyJN|x-ld%k4(Wflo@ZtKXLwi; z6rj2rQAFg`rIHsqrzfT|H|zF!h6hAMBwVUdH8hWND6rHstv`LISygl0*9lbc;?DKZgWj4Tgk?uYGoG(~CdrcFN$lKjP};@XHDD`ec@ znaFO>ZD4S^k6QEAOVB}hwEC`urpY>-cDyT>UTk$iB@5?ps&*qc22a)0g@UGg z#@Gy$bB?91n_=>4YE1F^B1f`P@csnCU-A(04)@t|s;N}A{7P_x_~qKhZ&lLNvK>7C zqs%o%WRLE|n%k%qHTK;3p!Qqv0<@n{{P80-=Ew~;j@CH-4n{={?AL<}=l0)e0;0mT z3vV?Ta}0}>MNo++Nj=JfC9u1+Fz3A)lZd{YYm%?U5D@^i{=7bkw`C?McVJZ}_MXCz z5>OKe;=f?l5ceSj`y|6%^PKx!CMO8^^i!tJD8l@^X>naL_2=c#IY1RLRlVZc_YSWC zR4;QqeOhDIgRv5?8frjBSHZa&^fe@bTx<_J#owB?((pdvbCq$rl$ND|sTs_?65_QR zC~xBRFL0o+V|7(+$=rwg{6{0OwWf z0wVUqYCf>ll4NjHlgt9W7T&~2+2*sN2eJt+shuQG_HSkz+k6u16|RBQCyo%NkSJaMt8UNu%xYmo7SzR7^RzcM|NFJY&oY_EP2 z8tx9EewgebH#yq@Grya&U&5=sjeKX$1=wJ3V2Kc~oVsy1ZEEi!Bn_Vz!Z4K?mvLUm z&T8g@Ox|A;0UN3W@m6&4!(3x;exHg(`*TVmot9~5%^{0v%H{AqM6=Yf?rH5|l8$qd!CNedf0-31DYzmZ`Yw^_R#ws8UPH=I5 z$9EP+`s0z3Pe=>hr7m8HkqSvKJxxKe6`xn5%ZH)%Ke=`J&$? zQ|l-4bYB>gMI2~kpAuhvV?BLU%d&TwftHmr7p+lp;R?+hui*RP!E&7i zx7POiOb1poYixz&fgN`3MIP<@TfVGOu?Oz&LsN^0KAkO=xu)Rftvr&B^H6_Tp#N&3 z{_kF?>_1YO;Gh2CeBuK7LLi75r0V4g@5_)D5(EEfl-^wMc}j}}T0*%%9Z5WPm9kBL ze2vd-&QWYnJmrBHMPEx7&KiYl-Gv~$!YccNkb7cb&Ny1ka0#zk5q>+k4RSe0*ay4^ zqNGq6qH|ZNvg{kO(6AYjON?V~N9m;&X5ZY&1yY(Eew>Y`ggm48Q@efw1N^J;_`F{u zu4$j!W$hsna(Vdyj_Vrn>DnMmaZ<$UEkYtXYke&SQxs_@+uKGDNc zqfd4q_ zE3MjLI}t}K(xsP^kB3H-tPpxM577=dH#q2FoR-0C6v`>Vvf69DBD%SAT$5_Y+*|b` z;U6y`EGJVMk=GDEDn^L3R$Fc{OBGEZg}wRfd{W)Go>`yWm1fQf8XQO2&zKw8`A)8~ zT2lQjBqHKhlEw>C7c+9k1EGWqYTXGI=S^>>1%>}VadRS-eC{d1$_i-p2?aK%;>oDd zhr6j0|L3%Z`p_I2bE1dyxA@c1Ed^%5bmTw&_W$nn+Td%X(qzG71{c^riE z8Y3nc*>QBn$AQ0#@ofXBy+qCo(jU+brB60@5m|%gJ!HN>3|lf7{u&FiT*m z_r}Qe_iui(zk@We2KUfpcwz#*g1yMs=u~fn)->cV%cB^4?T--%Z!-L%s<;|=hY=!YxH$8mY^?qj+`)w+SJYWRs8cU~ zbW3-jxm_gSQhkf-ImtK)s;%eN^aM5bf1X(WiNG&W5hVdtLW&De(iMy=E*1ycf1^#9 z$1I08J^q3~SP(@ui9#B~P7|0(_1;tep28Om9)hjR$^C z^o9iY)O2#oG*o>03h|)6tbu0tAY~!f)9FTan-k^R^Gs#q3=vh znR&B+wfJJgMJYyoj^FvNyT;{QNo#A-+xe}>ujb{<%=olDGm{h0ebTv1U1eZHFlKT< zL!)~FmqdI}H>0b7jU=+C|K}p&4OW1{;XQ5QdwSX`zEAd}&keiO&@1y*o*ykOoXg1E zS-iLf=E~cyCIa{I@L9q<4)O$6mLbr!$`gO9m_MC*Eh@=a8hEqw0!BU6$PfY~Z)EXO zs-Ix*TU>LqB+Xp{0_wU!m?xY2IAv#Fn<*msm|4a!V+DTer1&N{;4ZhAX!ip&b?GQ7 zHokvpsqdv{-x4x*Ch&_Tr%|zBRtC=L7Cb$c16OKI&SpaR08I9)Qz3$sh1%XVC>4;8 zvY%b$)I$l#Ca<7Yv1bX5KfZ<2{Y)_h^6iG|rVJ^ynIiZu)8q4h2B)(2)3AMCQvaZf zelX={kU<9@-q7YQtlYWmlyjt zwA37DP9=11d|X+A>CaP&xiAgzxe6L(_-=TLywx(&UW~j$u!pifTSVk*D>!Lu);?WL z*Em~V#>Q6IX;|56|3$PEm9kz{Jw+jcw^`WMX3!$RBSXgPExRSkm6U6el;$+KG8WQ_ z9HFO#54AOm2)BND-oe=%91!EgB@L* z3XyoOjRl5@Y|Z^4KBSNEmL;JE+^6&+0v0Yv3w#~U(EDIfLQ5cU!HvaCpB@C`51lS+ zm@_(M@a>*I9r1VWcBBgAiaAHXy~PxwZI?|BQcz7Y*o(+}mMS!(8?c2B_E3%Dvr}(X zJElqD2r_aYkX}}I^f4`InXM)A=RGj}CfE9zmey%_r_lqIHN!F89vX<_`!)3Z;ZQ7{ zO?%g-$wjtJ%gHQw^HG(sgb=@W4iZ#xA78A(nMXo<-4Gb zfDTe_+$T5q9Pl!GaBu)>1P1(5uFg=vfs1-9pJH^bXRa3|m>hPW0Y9c^8<^w}SP&r@ zieyLzGl3}hoxP4B0yW82x0fzn#Fi&q+Zgg4Bl!r#$F?%s1dX`r24`Vk@QaPMK73-e z3|T<$N9qG_ZdgScfka3@Lmi9vN)#*2X0MIJ(a?zLv+d0*FCRv)(pQSf^%%Ou`Uu(Z zLyj|;@f43JfrW7VF|cq@0G#(`ZA&HM(R|j}pRNLbgYtcG(+dG1F$y(sKnC%z`%4VO zD@iF-eA>ReHe^p_ii@D_`U5iFmgzCWtCm&yQ9}j51lpsMg3+{PyGuMpkvwUR{7!7& zrEwok9LcX3)Wqs#G@Xha2jP+?tB&wz3`bSYa)VmB)ub{J>WdqXMY>@d_i>0Ms4CZi z3xZr;my6-~k4o1(5_+MMlrox@|g!<5nk3rp_^zl8lDn0kNwd)ly<3> zbTOLW4}W$j@3DMcnsDB{fGhmNKKU|L=xyLmB)CuCSfqY3tfav8PJ3qtXaa}1G^>9) ziVL|+(hCHh2!8jKQwqYDcU+}@A@IY43Z(>Z)vJORvHqGbZ!mR*znQoTD1#Cm^WxK{ z+wq{#TCZ+JJn27G)) z-7Mo~$&m=(oRhZHY(2nj(TT$`H z@}+6fZ{c?!`xj{bDsrFEvA4+rSefg+NdVzP5mhEm5M;Xtn#I~Ii-nCD93lB^(45Xb-PSJty1%ptFYYifns%F zM~n03^JhPUFs9rO?;IW0?dI_6!P}nQ5`!jjMzw~-M`J#cB98bmF3iOYyKAe&ic<*f z_H84KnmIn#WuG3GjQXEM@F`Efl%ra*3#Y&TJtCJ>y;Q>k$Zd|2Yj~IzY)2PhT62mh zYd!)pB%M#r1K3NChJ+Qmg()*kB7QNOFS_^sBJC;fUmcbRrY%0DwHp{ae=DvIP(~)Sai2MTFmQv ze$)J)sZjje27NKi2mn+u2*oNtT{JHM0C;g(lH4`_wyP3(6h;96a5rSV0|6Qkji)ef za3yRkmXz;esnDz+t8C_oOeUgWCEOb*qG6$=akATNOvR6P#(K;1m$Md+k_V85N1L|R z`S<_FJ;JU(^&^%VJAV2k`ODby8d+3O?@EPdH#jzVJvE0&3dnsYt4pfc)dF#wPTK}J ztw90{XoIeKSo{da42a;T^vd;$@~SFpv{aFvNXF>?(n3u}c{e}+2cJP=7MmXnI>w<; zd7<1~*@G6++9`9q_;8~!DE&B6m5wwpWMXC zUwm)^`c2))+18vpOskvv45h?XSTofRg))CF~A7_%UbxiYiX> z*Dwe1`p9c}0kGghf~#ZMXa`nqz4l+0lIeNc)XJ8A56>vusoCY9uWKM;=(fsW@!3)} zRflZ3poCVx|BNnSq>Tkugg}}V(qGndA6Dyk=IS25hf0x*OL0x7DR|B%$vT%v1Ids^ zTku@ZMV>2%-MVJbA^lN_ChRvDzRy`CKw0rTX}RQ^qZ!#EIH`4QgNp!cBOmg)8p{e9#<5Nv2Qx%UJ5nu+n~^4*a4{e!v3e> z;NW%8Z7h%t%=dQX#Kw-^_s{;xaN-%_E?#fZjIxId2>p~Qevs9JTcXEXB;DM_c>I?M z<$a}5Kh?{CCXYdz<$3r@3`_k2F#c)@1_0Q4R>j(Ti;!LIzjI||6cnWj$|4WKc8{Pc zdsxWagIe9&DvWbLU)G57v5a65y;$e>jOx}aSFN)T-_CqP$hjPk0Pz&(?WY5?ReA~Gx`g>dw&A74a{YJiR)(kRDBZuq9XUB26pv5 zWiF&30nG1#QTm;C!kF&z#-@-OH$6QB>#Zq_?^Uq8mn0-51&igJ?hyAFS;z10VDbju zr6y5@bGBL+Tup8&9oT^4=f9aiEZFQ>@Rtdox0r!1|0 zyI`4H=fTD32FsxcmQu88yq7d~!`$1lWr{+iL#F;`r?id(XKFXv$1=SY5$iuQ3oJa6 z=clxa%;xixqe7?AWZ2Qfyp_6VEym#F5MV|BLpVQ&!OeP;xcj)S!nTM^A^THhWlqmT zl)~eAG&XienKi!KVpmSM=&lIr=JoHwQ;M`yn5ZwRx&$YD~3!i7XvdnBwHyE51+Uj$8t>$t+z6OPr&!;b@0nt=+!nIl0hjy_j0p7V{a>HYAAj z>#<1&*DcZ;+}7(;J;t0QVBx2VfNg$N641*PcszsX9ZZ?y$Rw1>duLaQK|Y?GOjav0 zq>e#V@O&n7!q#coDg9N2v-XwDfynCY+7ln<6n=jiPEa{e{gD`j@)UWo&XbQzlH}e84UAx*|AvCVSf7fY=vMH{IZcE@h zQEM#SZ?6Ol?;Jh2ywa_KQez?QMC{l+0Dd2N44tP>-HhO#XT zC@0#v9f!Z99dq4aqFCRxA@QQnq87kenEy6Wz}hg06K&j&lk<^~C=F{Mdo1^vl7E495`33kyYsad`wd2#6A)kwO%|hhgHtv1&j_bFg(a2R6mW_le%vQJ85cHW<>69Jk+cTT%ZbSE?F`mX;mQ4u2*u+$? z^^ZmLnTwb@y+ccEOcVfsySHZK%U3f)!p;B4B?W}&*mmX$yx}-q?AfKd7>$hO-Yeu}(ph|QB zik%Taxe>)9*sX)fW-UtOl}Ox%u>ZK<@Zz9JqfTwO3G{{jWJ71su;{wt1&>h!lk`o! z`M&7szY znJDnI*jXHgk56TOPAO@)CPQ3*NEt@CF%^n?6u&u*}uw- z>?(hKJPy1$Wco871CGunz20G@pI|$T19q&-^BDZ2z$%O7lud@jhBk(6a|S<9Th(Sk z@93g$opw7~Siz;}_RJLL+Ec>T_yv)%N<$p8)h7;gCfg zARX)v0lCE=$}h`Ee-BHXgu(J(45g0l0I{cRiY80fhvzxWtK+fh(U~t4KY_-Z#zq5B zh0HHUK^f;<_xhdC(>W4bZETzIt^cRHuMTRXjrt7~D^e(0poLOeTuO0op%jX{d!c9u zQlK~$pm_1(8XST<1SuNaJy1L}xI^IX`+jogesll0f8U+SOxVqY&DlNYIX`)x-K`)Q zB8i7;(ssRT)Qs<~b#q3E3EJ-lA7HW2bouMMBpnXNqCS=D4eQfQtPW4blGep|_C4pN zajS0HTMMAG^u8OFw2o^->+o$+SY;_kN~I#bo#mDCJ$3(&87u&o&hdYha1dDkFD@X* zCt|W%RIoq8{}-bV3CI7CjPBnj?qI8iJ@(xck@}?IKu>{q*IIyE-!I|)tn%6Q(`5ha zD?kupFUwa!uiD$8MqL(9GCn$j-}G>b8YgPTC%nkvGQuqtb+Pi`Y;yvaKfNAR)v`9e z;;&piFm)GwET7pCIT4+%izu>3TX$OM&*m+IO?Dr}bLX%x+u8GKk?L9QjIM_@q1+hu z#>*xIRI9^s=mZZRkG5~+GzRyfL=P=&`^h2e^FE^Ev8-k#wxy*^vIzGkLl#vyl%1pBoy-u-|q`}A(K zBI3@M#RL1BK0m7+{>h{IINeCn`{K)*I{qF$1fXC_olEWYsg?#}Ns1>*T;`*4M+_sJ zBar74Jzv|;+NvVnCYw+kpTk{31$KlOTRCKBimxW1q4) z{zZ&Rvhu%fqTUMzc+oNck*3oEIUeZqw384jj$iH`=w-KoCSran2vS^oUbEG!eWYPG zRa_F5<11HL5ge|b{u{P8Q6L6-8T9(l(LvvBYHB>iL1TlCd6;8Ue5yt>U|(HkD`gW( z`=>q*M+`YM5@cCTrwVvzN~{q|V-tPkafu!DG>>PSS+mX;$5Bu4qSGbn3JMj&*^8Sd z;@NE;Nb|nG|76yJ3is^`y_ceJR^?wNYFYIxVP->Jy5jG2sPeb>V0)4`iOEbjh{K|HdM{lGHyDK;xVNv|W3nQfFP<9Zea7ss zH2aFj`Msk4?Dy3m7PAZPbmY%gxRb0*xL4%&`9`Yu_#f3~`{^nCeYHO*o$iDS!48Ai z_BE=$HyCMV_4&r*shWuy>?)M9__>}bn|?Eunz{smYC`WPG0)t}6riucq3D8=gy3nT zGfTzp)FkL&K=KP#247&}F-9V~%AML}u=H9ep;Uri$%I-sb!o6_v7RtS3MuH*;WZOz z_HDj3Tv>$rOt-Ts_rc#Iw=AqcZWWb2@`n35{h-Nfr1E1Wn&X`B0{0KswLoEu-;KNc zV|U9tLq)mb7e^AwUqV6ap}lox9&|#*q9FD7@&rL^dS=&`dW#A?mh&ktpdWy3>g~2g zN7-K!2YRx^=T`6E%TfU6mZY6EIisK#OU--w=&bAPy|lu3`5m2vXWb-vJMHR<`3vci zU04!4v(KKa0Rn}QkboRJGTnL{#jmXFTl24EGpb^6dOIt>-lCSf>>ce4V{dujMTmoR z>g_DIU;Son4np81S98p0*_P80@1{`t{6GpG$YeuY!NYe}WD@HiU58lK$n)26>p4{P z)&Z7!dbyf}k$wkp9l&pT|Fa1bNPSyPjk7M#r|Kz^qX%^5dZUEgy^ydw?OVzoQbg4;> za1S=L3OhnLKFpeM)s%B=8@ApoGC*d@67<>n1Du}<+~|{!{65&nj*{K7Aer#LFMh;U zzwX{Scj~YwcD7&8*d@Ewe2O|_T=5OSjBJsIhw-m@Kz{H{wk>8=#|*?8mB5o3{CO%B z^34OWSDfAbt-{91E2pKbK_G2SQLRF3G!2;{|u(>1F}WGxKL2|88fs z&BRXXH(N0wjIR5Y?YULAi3LtCDd{Jz1-m>`hSRKrX~9#Sus3II6=ugq4^HdyI5;wQ z4H8TYMbiM3NgGyYFX*;Aor?dgD<3=*`;xL-(9KThsq@Stp6=(G?i(#>dYF8@9{y_r6cAfTSpdWK4t*x2i>>l&H=UOxJW~WyM54jLPETHr6+S^ z(uA*1w{qSD*h9m-A)>W;h@6A8(NV8&okTxWmi8v5aRi0GDL>(+n51Q_EVS(pJ~X>! zbKV!2heI~`Fh2J{fpudVxY2YB)S4F`E<}*H;5aiKNf5-buZ=Y!>kJ~OXAjo2P6aqjM48$1ytF+GD~nUr6t z=HfI38_Zur4Ft~}a?*}Vd!r)BBX>%K?rs`uDnR=ZI8 z*hW6a<%L)WR4XJ!hVb>7a}d%Kiio<~2De*H!zZ?;k)(nDSg^KvnZk0?SmBV?_I;Ed z4KwMI;y%RaWNo$J1sCUgiB@LC>B16uDv0T&=jGy+k-G@sqLm2&8ryTnO9ERc&+z5m6C#PZnvpxw%QZyNw2nCiC0qCvG^4Ncx=JOoA`fzaB4>i`T$(=Zyz;yHl&#K|pcoe$8^tIM%)=|!=3o!Q z2Fd)0jEpQO*rGA>9V<1cwwuV8lb45?!A-N(eNn-=0o277UBN&|?`*NKu$G!VZ487s zIT3Snv>*Z|N;zw5Yf%vqP5{cwilbx~^Qt+;BY)RRY#F4p_w)RulmKYR6Rd~k5^ zXIf%n;ypa#i&^v=o$*}x=)k~0%=P*HLOm}N)0f`0!%JU==z{bRi$_q+B6T({Zf-wc zUuquXU+L+Repe@IQy))8euJ+kQr#ZQX}R)kohg!*-#03=i|I4zT&?mB@ReLfomHk@ z*meK@{b^@BDBn(AUS28_*haRFfmIe;TP2y^a#2hB-&{!FFjLWnl$DjiVEajC{zVLW zz^oSX(C1=eSHPvTv$Mloo$k)p?zVUxD=H~TyDQys5D^h^a&n$Jgyjqjg2`ka1#8>| z>)y@GqOb97k!_fZ!?pCZG~1Mq2D;V4pPqd$thWC6(Fr(kw1Hr(1#=tLm>YPUqNbk3 z^bM(u^k-W9LL~8Kin-g5X1&SWk`LwRfNt0KirzaHZb+VGqf=GgNalx-b27Y4V0d`< zQ>HYg6gKCzpmmM7)^3e>qK=1-x)_Snot8GSN11$DJ_K6o8Wmq^3FAEC`9xkrm-xX#l zsWxjwBJkguT{i~O#XVo~^B*4{1B|o>gH$HO!NCCxUMkec{Wz53<>iIJ|N8Nye(mek z7Ovcjj2b=4BgvjTyJw_@BArjagVd{hGxqB5IOprY4t911x$j6Ggj569l2fm|p`iiP zzTE0V8%!(h@0y1`&z#*NR&Lnn8{J^ohI z)kXdIVLOpe65YE{WsLyxkHaAE67q&%42Q9?u@Svce|~zZbI0}3KuAE~(@Oi7%$hHm z3`vM5g7%X@DnEE4Ch+;d{6R>rBz<0XsKf(CDK0XCcae168~!Ks1YBHP`iWG8K}${U zyFi|Uesur&^T#sSVlqyj3z3qNGCx0WzgeT7$PIr+#bns}^-UVvV1uIyL1mF%sSOb} z=(R??@}O7EQjj(_PI=p{!+B=gBl_#!7;d6q0dfs%SM|-);M)pxz*yy0^u8fqQ38oc z9YHItRD=DuLExv{TpV&Fa4<8kjYE};dGykUw~l#DTiB?>*~9*+E`R5$OVrsaHlG}c zIe80S%p$%lj2oUM9I?v~ta?$`5vtLB=BnIp8oXNOcuK{B;zSl3t!IY7i@Nv=1v{rX z)%G8w28(3yKdm)I&bhgIxoPF<+KbG)9Gp#*ED%KML$M?zBv_Px{fvuS+uAw@Qow$y z_&(0p!ootJ#)ZFqmY^u;y51K;%2>t4Ngb}DLNeDVL4ype#m}+)@L?o#wr#y6m|9yd zG!w53YEb?09{z)xiVCvOw?L8%3~&etXp7705N?W)F0~jr%xZl!{YC?pa^*Suv$LcW zGQ-cgU~REW70}b>W);kuNk%rgEhK()z*tG6dQ11JYSHcLe@cDK9S! zh_P#EPnDqT4^4@dx2=!s{M~g+)r2IDaUBp9*OjyT z8c{X5U!k62r+FbWQq1R%9?K5LYfh99YrSjivr#{XbiI{h-h8)F1>tv2$1lC{dUuaS z>Z*@K9J!pU2VPO8t3OO8CLTWM(4jAEX!td)XEy}~FUDicB-JnEH7BuYT5MMQ;q7}mstWpXW zA*O#Uj(8wZpAQI=YV^7XllhgDl#+N%6bZ&k=e5`*g=i;ZqwU0nsL>2x$S zdfM9BUSA$>`Ag$@de_>YRqW2OoZ|6Z@LBb&b#S^~$Ey)H!tO+_9sz~HdPR~84~dd7 znzoy&i`>^++f8BcPVI3&G@{~@aFadwajAKJdmLbVJG2JVmHOGX@Wa6b@y36v;63Nw zdo0k8DiJj-7Sp)S&H9E)tMRWkiIU&+%JqK=x;lNjy)GcZF8xrSu_wNk*^dPZBp2L4 zE>gZG44{$uaiy9GBrcC(jV&;iGBGioot>pzlFAT-boCDm5I%TdTKzH7TS^(IGmWJB^^K1O%_k*$5OhSvcO%yy~cR0_UBTLkaFJ&Pa z!bNyLJ(Hi~BBv&$OPvN$U8uUHNH5~OSOlg@OA+RnjphxNO z@r=Xl^=~)ZsWGZqAB*FFFn?|l((8I+`?;=(6GzqlRN^#y^p)UB`|%GiX$FK)c(qtv zj!FHVV7;Ut2TY)gVcK)6&O*p@x>V)#$*LE4Sh$&}1rHyGcz?y3m+gJC7nkc)orABW z4!u24>2-B=@ktpd32@eWBCj`+%mg5jkNCdk@5IK&j^)ZDHe4wvD7wO)=PyI4L5(qI zE|rgmt+%OIJhq=#zoc1kT{)$`rnhNmX;wz!#?qy)!>Zh$ONG&FH#3yqa@}Y8?m<_z zv2}$Xz`iG4?!zp9bo6(AUoYdmR&s-TrW8aG7VJ@_mlXB;mlh5IQ+$Og<6Tyz$?lxn z5kvYMdDEV9SP-Od7!sBkVepW3bBdwf%LJjNbwdi}P@x+Zud3{5Tm5jJU$ophT<0Wd z_i@Qx&xvN%FG8_aTp@MJcCIYkliP^G+5*)zD_#DZrZRDvIaqY5?U}=3T}Jqib*U&( zmpvDz+*U0Db?ljpxT%NOZVJ47DL z9l1>ut~nyJ(M#L$ZZWHsN%X_bmioNQO zJ)lHNuL&R?k4MVRS5l&TR5IOJ7Yq>q}~|nW4zw&UMG{~^tF7BJK8bvK}^WM0%et@oWW8bx{qXwV#yF- zh3O6T-fg{UrJL)IgFPWOtGxDjL*<#U*-OmyePjz5i@ky2klU!vajB`6Wh8-94;U78 zVWvY{$Aa~ZGEk*YDiSBB!ypP4<;P{RMfHG zgdGH%zA)Kc)Fn0XH8Q+FIla}U)6Vx)TiuJ?hT-gq2>3Ir}-y$2Gl+o z+Hkxn8z!*|7@YIt*1;pI_dI?9|EVg7t2cXek5t(`qUZc9syELJc{=&wXpbOd)Iy0d zWmlBruC43rLss>lx3J7s;Sh}g8N=iDb>3hbp9jN5xu>u?v#2vyGQVGo$Jy0zq8~pv zpS^Y&lEV&+ZLut4$IfWA5e$r7x8fsuitvpgGS^!$HP^>0Kd-O$ZelwMpS71vDm~pd zZC*!pU^HN*hLMB@hY1cb8O0>l_Vye}2nt|sYnIRX9tErFCjf$YdS(hkGV1GvK@xsf z@i}bYm`Oodc}Zx?0OuyRq*`DZy(ctt8sVkiE z2^2nEndPMydeXpg_E#_~nHwKu>o#)cWU%D{UToNJTCDzHv{gkE%U1o4nQqZUs7f!h zUp!pgpFh9tes9|rX}0Xnnp?=(Uui#ikFD-(v#E6~P;Z;8S@j33Z&zw_%*W}?P7#iX z-~O03NrfiW$NJ%!K_3goxeO3|*&hpE0q!+Hm#o_g zw~wbz$;ZXUG7IY$_>L3!!=FheHFJC9m*jus=J6~{EEo4*NT?;zOF!xcw z8QTpTn!>o~lrfj1`*6njwxiT?Cl*TZrlEZN`OBhs5Qy#79+aGw_u6%Lo)VUmV-3Fc zPR0g}>JDAcyccufR&&Go)$m}io8i~SjL+|Anlkx20eCf7CsQY9yz?b-62jfn6Yjh! zqpTbOAVH1KzKdA?!CW~TXVj}5< zrQA{4=H@F|Z@$gfLy!P40Ghgb07IBGd!V$e_4R)N?Sz!y)t7ttHC_3H@poj%E;q(X zSC@jn=!xrd2AZ?N)!$AV4w9LcD@ z$1~F|nQZm^(ipCMmXi88$FQS1jHm5oYz@BfI4F=>knVCV)`|K^{2^Q_u`S$|J%gR` z^)ro%?_|0>8rbT-T0C+DMh19MO(vqfH}dj}+^Y>yvvX;rp}Sk$bD6^_X3DqunG_B^ zd3vVqR1pBC8XE)J!f}Bms1gk$BjeDNEzlHfY)n;t=mn6}Bh#!`T0fKTgT9;97pkb= zH?wfvdK&#i7G#@%O~CY4l@HWz)A&)0oE=loNRAC!M~~pMoL-z2>b>CjOy|Y~Hpb7k z10#9MoXSlDgxY?*zP%NMpDvBXsOxgeyR*$nTOiTOFWS4gSGW9V&Yh9Y`xYm~f^E%- zn8h#a;7ngGoi_ld31dtTxHgaTgn6OtrFEfnHC|xIR_E@*etx(CnRIBYet>2#US0h@ zxw#n(^j?>tEhAEXM7`~l3V$5$~Ra*87cawugejy zD)(8yp4Vq@m?3@?s4wI1Qh>PGKp1Sf?-A#c`}VB^y8_SB!2SoYj7B1rKyz^t--WMK z*GqPG_Ib2%g*jH&O8`H%`eT|{Dl00~IT8qiWLh&NVLd;gZYYy$JLTVuJNYW`2L^4O zcVpWJX>A)6ddnPR@p1|qCM>0z?>~DynBsbRyV}wIM#HyA8KT9}URApJF;Nd@%5Bo5 zeSH)b9UbkRFtrd&YZ^LTfp2JT$Kkw(lz&w9%sF>pf>-48(BGRF1dPT`1}7>g@|8F< zW<0;vfrvXblEAcOSX1I-l4xdMSYAYvVM3ll2R}{A48bXl=RjlOp9=reAWw*5nbB!U zg7YWqH_i7^9i(q{ye*az!{5KaDMu4un?EiD0?&|u-8Q@r|N)@56N*U0k8mU~` z%+XuHb&&aHRw@7U>mJSXfv|!h_`$XR6c=Oa;2#p1-pxoVdUOl@(;iecd2|{TeIN z8T9%+yApzR{ED$vGZ^Kr_~lAlRUaGl*bRRoCy7?mU36ga`(ruzl^RDb*VA8e3De4habl*lxo8QR1NNxadX{adUV zK$6rbo9%sy;zA^P5hq=}d4Na!GQ+K)Eu%=BCPS6YGJCsck0c52W_@P+clA-I`wX&) zeQdd=ibr;*Ea8?DkCgGDhhyVQ%)r>vg6AF6ZM(xb>NFNLgDOc(7jqwPskNWtXl(gg zsyy}ZT}Opnfu57d@oU1LKs9LIW5TrRn!&Sos-9U~$7e4fZ&(GNm3GHK@4;uEa2DWD zW77o!Kc(mgOgU!vkz!cQv5l$x8N}9wwGxb^8#zaM57qh1bbQqlg$QX{!TT(X7E;F4 zp)0bjPTCk|)@RQw`+hR1s;Y7!gdoet7;{-YJv~sNRzXx_<0WdU5#id(Ss0gjHj`bznv+eS4U_ zWL%xsCx>%}g`&P(t1WX0ak2?J4rXjg&czw!J7}36oJn{@!#bw1F76#Y06(n2JQ~1b zj7ZZ;>%=YN8+`RNU}&hp@q( zLZ!Fa()Wc|(!SBiWpChnx#=0m>(IazLraGRvY5=QEfy(x)ZWv*6ztO)PbG-)_LeS) z$lX296DBFnLve_>{CVr^>#L!mVQ+6APR$MATY%668ve}G)P)9@H2_(~$5R5tmha^e z!06Ib>W)VsP6p4GbY5(shaClI5THN>ipEhni=7@#QoD?50IjC~)qnQN=bAaxu@$pt##cEkyjm`lCLJ`zg6e17Dj8k!|X@^H(W{odKI5zL)~FOrB#2(l4)HPt<4^d#W&7&|(X0 zp{soYWwqKXn#fB&y&e6?XQPBzxk-p4&3J|__f64$NtyPai z1s@+jKq1?@vb(6in`0>ODHozD;_PRhoV%a+yi`Ls-3&~o9*Nrt27 z2WX_SjmYZV#&%d^@*okbTN~YMsfRIRQg8EKSZHtad4o{xV;t?ASkS=})NIUGE|;kp zUvJcR4rpw>F{fU%Ia!*AxC{HtM%{qDJIzy{!)DN(MtyU zYJwmcX=&*$VXwnK-N0>HqOV4)Y*#*rTDt#xx@!-uLo?Jui=3VcGg@6`cgF8b6lLET zNLFl|EFV{gaTTvOl9)ES)VS`qWWyyt^Y7%lt!U&lEj1^qaGh(|GxT@Ympk?AprkqO zh5;DCc3p4cB(IGR>a>+(r2HaGq;?-fDItstN^K$sDcnvJ|5;Tvs5vkjouPdyp1$Cy zZO#w+*?Ix~V7!~~&`h*#{*~mx=d+D9Qky|15q?YSuIEOXLcR*oVsU+wk1ySzv{vyS zC0{UQDMw2=L;j?(Oz83ydB_>UkVz!UjIf0saCt7u9iLpvyxy&Gk%rflOlH!vSpllq zXgs{?jD!8;V}sLnZQj=Rd)25Mi4OLYA9_I> z-rXg46}wdt#07xwhIXpq^hX?jK9R#s@3I*2ZH5@zN-H);adTH@irjc>36KmDJl=mt zqMFUdhkP0|;;c)3dU$*prJsR<*UYwY1FfSaX;xw6L?IrJ%^=ZPNK@Dskp}GYh$v z4i!nV+r-quqXoRrx{?|Dal+>HO`_(f-R*xp5j9lud~9$#3l=CDx1sYkA;%`(KoOOJkd zG;zC|{PkTI9L*+9bCUP8Z9Td(vo~OBef5kcQIh4XzJ>l_(unQ(F}hFF+C(ga&qeX| zlWL#r^WT4)vE&1VjxRkFdk4|y+NNFKZj>#*L8vQr7IcWa6rkg7Tf^=< ztOn=R4MY;a+KaiP;<38n=S=qXn9ESNKSL0;rO`Rz9Q zUFaFg5I4lewr-jrfWOpz^Ks>r&WJi|Li%=1wdUp!OOzX&b~8XqZ+qoA4#-^bSkaxh zMZTNm7HDI9b^30Sa| z%xlgJ<0cAjv|ETuxLRj7D;_Q9RTbvfEN>4E?&h2*O;aTe!p6#rL_vYF;ZA17akKV( z3wu8vqsyKsaW_yFj^ELUL`t7GucDi4 zwTxF(Fp08+6Av{5TdBY!FY9k_Kf+mxh{5ozv14Fg5^i zlqS6U>UN&H(v}JEN)|pAYiq3lJ9ykR$o(}Qw@jIB%>DkJ->+)uY)mN(_em(@O)Z6_HfkVT>66!4~)WVLl zqjPhj%*?Xs3L4tV%EZyV6E`_)&LVAXZ2~I$bJg;Yx=?#Kv)aQW;;Hb7eL-8(~l{YSqpcVY0`cKhd~ z)5H1?E6mTLG@?EY=T=)(&mei2zFxJq87-oI7w+DF0rk{VGrAgD>@OkZuKW3MQL@!? zx~KWYcZs>j2XQ-pg7EKZw4R$Si95J_GR1{E&MlVonhhDO_z&M{0G+VAFErX;>NjoW z*|uxR_yrODJ-y!067M=aHcG%1?T(rRdiE(FdTB&ed3iY$3I%pb0u3!!$>7Se&R|D} z1t8|Ks%1#{oH+vh{@Z7j_4PENZ|7HIv^luh*naj~MMXuaqza`=`qYJohg)>WzW^h} zY@l8THD#<{p)j63B&NND6BKvaJHI3qp7eN|Rjt|m^KP9yKT7A*XH*lfzTU89{~Fw( z(CrDmmw}-;?-IJ?J%`k>%AGZUuZcfJ_MC?^n{QCGz!>2NJIQEHRewpF{%94}@8#f@ zia+`P=E-Xn5#SM8PY;ef7Y26|JxCOHqx*A2AuQtYFHRDIYHNgFy2bij7tpi`cr^?e zAO+3?kke&GjmCR()i%Q5c>(W}ZNQ@6zj0KgRp5{E$PU1VKp+7DcYullnXf6zDdF(< z_Xm_+QQ)=O6%x7F63rwbVSG%f2Z*eTGhHtu04p<&DKG>JcwFCN&DYrRo412#XlS~U zVq8ShPEJk$R0*&V=-fImAl&`KrNW012qmBcRaPGC>l@_~fwBz%5*(mk0*wX(1B6E& zI2!;*VEoEnI?(%{q^7R*+$P>)oxi-LKa|M-Yp~m5+4o000J&x*CCW2$G9*DW!~|NI zul%{$=H)Ghd*GNUn@oZt9$ZTyYrYyEUggDl`Y}=dQbwB5W+dbE@X*D@rAQB!k(!$N z_E~6X=;dUj%pIMu)1t`dgOv)4Elw@Q&0awA)`P)L^MzR}-XV1JudNew(cJ53RNSn)Mt6x+yT9un#m->SNdF#W6 zi5k0!uvZSQiI>&&1YbUOuw#J;378W1G(m)cme!NU%pP89$_TEt>eAEYCqZ{voC=wp zz!h4kb=cb25QHpCXlZE`X?yRCLq~iwpLV%4E;7qU{rdIm)7~N=>|X$72(^-6@dyIh z2b>ielnK9pN0yOe>llF!N1%T7;fhc~Vq(Xs;)E;s_n##6LTr@#)awBmA+c96tc-Q& z-sKOhs%eElP_8`ayTHpWmk{u5a{^Ol|HXxx$1hD}d8AZ?I@@1B$d!?iQCA-Y1eszz znE&m~C4l<^{<&=^Lf?+(y^jO#En&Oy-}CcyAhs|Lc6P7Jqm5dJnVg)Q0b{K@y@d)3 zSve(2gw`F}_GBbmvxtz0sM&LW`9In{-3(Dq&Ru}PvKdZeXJdQmn@xJ9Y<_TOPaAQH)ANh97H$|EsG=T{px4z8ZwKwKE`VH%KJfTY O(0e)6x8*YCU;h^}@W~GV literal 0 HcmV?d00001 diff --git a/vendor/doctrine/doctrine-orm-module/phpunit.xml b/vendor/doctrine/doctrine-orm-module/phpunit.xml new file mode 100644 index 00000000..77c3fc1a --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/phpunit.xml @@ -0,0 +1,25 @@ + + + + ./tests + + + + ./src + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/MappingCollector.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/MappingCollector.php new file mode 100644 index 00000000..2340e432 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/MappingCollector.php @@ -0,0 +1,139 @@ +. + */ + +namespace DoctrineORMModule\Collector; + +use ZendDeveloperTools\Collector\CollectorInterface; +use ZendDeveloperTools\Collector\AutoHideInterface;; + +use Zend\Mvc\MvcEvent; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; + +/** + * Collector to be used in ZendDeveloperTools to record and display mapping information + * + * @license MIT + * @link www.doctrine-project.org + * @author Marco Pivetta + */ +class MappingCollector implements CollectorInterface, AutoHideInterface, \Serializable +{ + /** + * Collector priority + */ + const PRIORITY = 10; + + /** + * @var string + */ + protected $name; + + /** + * @var ClassMetadataFactory|null + */ + protected $classMetadataFactory = array(); + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata[] indexed by class name + */ + protected $classes = array(); + + /** + * @param ClassMetadataFactory $classMetadataFactory + * @param string $name + */ + public function __construct(ClassMetadataFactory $classMetadataFactory, $name) + { + $this->classMetadataFactory = $classMetadataFactory; + $this->name = (string) $name; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function getPriority() + { + return static::PRIORITY; + } + + /** + * {@inheritDoc} + */ + public function collect(MvcEvent $mvcEvent) + { + if (!$this->classMetadataFactory) { + return; + } + + /* @var $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata[] */ + $metadata = $this->classMetadataFactory->getAllMetadata(); + $this->classes = array(); + + foreach ($metadata as $class) { + $this->classes[$class->getName()] = $class; + } + } + + /** + * {@inheritDoc} + */ + public function canHide() + { + return empty($this->classes); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return serialize(array( + 'name' => $this->name, + 'classes' => $this->classes, + )); + } + + /** + * {@inheritDoc} + */ + public function unserialize($serialized) + { + $data = unserialize($serialized); + $this->name = $data['name']; + $this->classes = $data['classes']; + } + + /** + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata[] + */ + public function getClasses() + { + return $this->classes; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/SQLLoggerCollector.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/SQLLoggerCollector.php new file mode 100644 index 00000000..bbd19700 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Collector/SQLLoggerCollector.php @@ -0,0 +1,123 @@ +. + */ + +namespace DoctrineORMModule\Collector; + +use ZendDeveloperTools\Collector\CollectorInterface; +use ZendDeveloperTools\Collector\AutoHideInterface; + +use Zend\Mvc\MvcEvent; + +use Doctrine\DBAL\Logging\DebugStack; + +/** + * Collector to be used in ZendDeveloperTools to record and display SQL queries + * + * @license MIT + * @link www.doctrine-project.org + * @author Marco Pivetta + */ +class SQLLoggerCollector implements CollectorInterface, AutoHideInterface +{ + /** + * Collector priority + */ + const PRIORITY = 10; + + /** + * @var DebugStack + */ + protected $sqlLogger; + + /** + * @var string + */ + protected $name; + + /** + * @param DebugStack $sqlLogger + * @param string $name + */ + public function __construct(DebugStack $sqlLogger, $name) + { + $this->sqlLogger = $sqlLogger; + $this->name = (string) $name; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function getPriority() + { + return static::PRIORITY; + } + + /** + * {@inheritDoc} + */ + public function collect(MvcEvent $mvcEvent) + { + } + + /** + * {@inheritDoc} + */ + public function canHide() + { + return empty($this->sqlLogger->queries); + } + + /** + * @return int + */ + public function getQueryCount() + { + return count($this->sqlLogger->queries); + } + + /** + * @return array + */ + public function getQueries() + { + return $this->sqlLogger->queries; + } + + /** + * @return float + */ + public function getQueryTime() + { + $time = 0.0; + + foreach ($this->sqlLogger->queries as $query) { + $time += $query['executionMS']; + } + + return $time; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/AnnotationBuilder.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/AnnotationBuilder.php new file mode 100644 index 00000000..1d7463e1 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/AnnotationBuilder.php @@ -0,0 +1,61 @@ +em = $em; + } + + /** + * Set annotation manager to use when building form from annotations + * + * @param AnnotationManager $annotationManager + * @return AnnotationBuilder + */ + public function setAnnotationManager(AnnotationManager $annotationManager) + { + parent::setAnnotationManager($annotationManager); + + $parser = new Parser\DoctrineAnnotationParser(); + $parser->registerAnnotation('Doctrine\ORM\Mapping\Column'); + $parser->registerAnnotation('Doctrine\ORM\Mapping\GeneratedValue'); + + $this->annotationManager->attach($parser); + + return $this; + } + + /** + * Set event manager instance + * + * @param EventManagerInterface $events + * @return self + */ + public function setEventManager(EventManagerInterface $events) + { + parent::setEventManager($events); + + $this->getEventManager()->attach(new ElementAnnotationsListener); + + return $this; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/ElementAnnotationsListener.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/ElementAnnotationsListener.php new file mode 100644 index 00000000..57ba69eb --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Annotation/ElementAnnotationsListener.php @@ -0,0 +1,237 @@ +listeners as $index => $listener) { + if (false !== $events->detach($listener)) { + unset($this->listeners[$index]); + } + } + } + + /** + * Attach listeners + * + * @param EventManagerInterface $events + * @return void + */ + public function attach(EventManagerInterface $events) + { + $this->listeners[] = $events->attach('configureElement', array($this, 'handleAttributesAnnotation')); + $this->listeners[] = $events->attach('configureElement', array($this, 'handleFilterAnnotation')); + $this->listeners[] = $events->attach('configureElement', array($this, 'handleRequiredAnnotation')); + $this->listeners[] = $events->attach('configureElement', array($this, 'handleTypeAnnotation')); + $this->listeners[] = $events->attach('configureElement', array($this, 'handleValidatorAnnotation')); + + $this->listeners[] = $events->attach('checkForExclude', array($this, 'handleExcludeAnnotation')); + } + + /** + * Handle the Attributes annotation + * + * Sets the attributes array of the element specification. + * + * @param \Zend\EventManager\EventInterface $e + * @return void + */ + public function handleAttributesAnnotation($e) + { + $annotation = $e->getParam('annotation'); + if (!$annotation instanceof Column) { + return; + } + + $elementSpec = $e->getParam('elementSpec'); + switch ($annotation->type) { + case 'bool': + case 'boolean': + $elementSpec['spec']['attributes']['type'] = 'checkbox'; + break; + case 'text': + $elementSpec['spec']['attributes']['type'] = 'textarea'; + break; + } + } + + /** + * Handle the AllowEmpty annotation + * + * Sets the allow_empty flag on the input specification array. + * + * @param \Zend\EventManager\EventInterface $e + * @return bool + */ + public function handleExcludeAnnotation($e) + { + $annotations = $e->getParam('annotations'); + foreach ($annotations as $annotation) { + if ($annotation instanceof GeneratedValue) { + if ($annotation->strategy == 'AUTO') { + return true; + } + } + } + + return false; + } + + /** + * Handle the Filter annotation + * + * Adds a filter to the filter chain specification for the input. + * + * @param \Zend\EventManager\EventInterface $e + * @return void + */ + public function handleFilterAnnotation($e) + { + $annotation = $e->getParam('annotation'); + if (!$annotation instanceof Column) { + return; + } + + $inputSpec = $e->getParam('inputSpec'); + if (!isset($inputSpec['filters'])) { + $inputSpec['filters'] = array(); + } + + switch ($annotation->type) { + case 'bool': + case 'boolean': + $inputSpec['filters'][] = array('name' => 'Boolean'); + break; + case 'bigint': + case 'integer': + case 'smallint': + $inputSpec['filters'][] = array('name' => 'Int'); + break; + case 'datetime': + case 'datetimetz': + case 'date': + case 'time': + case 'string': + case 'text': + $inputSpec['filters'][] = array('name' => 'StringTrim'); + break; + } + } + + /** + * Handle the Required annotation + * + * Sets the required flag on the input based on the annotation value. + * + * @param \Zend\EventManager\EventInterface $e + * @return void + */ + public function handleRequiredAnnotation($e) + { + $annotation = $e->getParam('annotation'); + if (!$annotation instanceof Column) { + return; + } + + $inputSpec = $e->getParam('inputSpec'); + $inputSpec['required'] = (bool) !$annotation->nullable; + } + + /** + * Handle the Type annotation + * + * Sets the element class type to use in the element specification. + * + * @param \Zend\EventManager\EventInterface $e + * @return void + */ + public function handleTypeAnnotation($e) + { + $annotation = $e->getParam('annotation'); + if (!$annotation instanceof Column) { + return; + } + + $type = $annotation->type; + switch ($type) { + case 'bool': + case 'boolean': + $type = 'Zend\Form\Element\Checkbox'; + break; + default: + $type = 'Zend\Form\Element'; + break; + } + + $elementSpec = $e->getParam('elementSpec'); + $elementSpec['spec']['type'] = $type; + } + + /** + * Handle the Validator annotation + * + * Adds a validator to the validator chain of the input specification. + * + * @param \Zend\EventManager\EventInterface $e + * @return void + */ + public function handleValidatorAnnotation($e) + { + $annotation = $e->getParam('annotation'); + if (!$annotation instanceof Column) { + return; + } + + $inputSpec = $e->getParam('inputSpec'); + if (!isset($inputSpec['validators'])) { + $inputSpec['validators'] = array(); + } + + switch ($annotation->type) { + case 'bool': + case 'boolean': + $inputSpec['validators'][] = array( + 'name' => 'InArray', + 'options' => array('haystack' => array('0', '1')) + ); + break; + case 'float': + $inputSpec['validators'][] = array('name' => 'Float'); + break; + case 'bigint': + case 'integer': + case 'smallint': + $inputSpec['validators'][] = array('name' => 'Int'); + break; + case 'date': + $inputSpec['validators'][] = array('name' => 'Date'); + break; + case 'string': + if ($annotation->length) { + $inputSpec['validators'][] = array( + 'name' => 'StringLength', + 'options' => array('max' => $annotation->length) + ); + } + break; + } + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/DoctrineEntity.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/DoctrineEntity.php new file mode 100644 index 00000000..31394193 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/DoctrineEntity.php @@ -0,0 +1,27 @@ +. + */ + +namespace DoctrineORMModule\Form\Element; + +/** + * @deprecated This is deprecated, please use EntitySelect instead + */ +class DoctrineEntity extends EntitySelect +{ +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityMultiCheckbox.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityMultiCheckbox.php new file mode 100644 index 00000000..1a09880b --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityMultiCheckbox.php @@ -0,0 +1,26 @@ +. + */ + +namespace DoctrineORMModule\Form\Element; + +use DoctrineModule\Form\Element; + +class EntityMultiCheckbox extends Element\ObjectMultiCheckbox +{ +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityRadio.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityRadio.php new file mode 100644 index 00000000..011b6cc3 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntityRadio.php @@ -0,0 +1,26 @@ +. + */ + +namespace DoctrineORMModule\Form\Element; + +use DoctrineModule\Form\Element; + +class EntityRadio extends Element\ObjectRadio +{ +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntitySelect.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntitySelect.php new file mode 100644 index 00000000..c84548b0 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Form/Element/EntitySelect.php @@ -0,0 +1,26 @@ +. + */ + +namespace DoctrineORMModule\Form\Element; + +use DoctrineModule\Form\Element; + +class EntitySelect extends Element\ObjectSelect +{ +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Module.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Module.php new file mode 100644 index 00000000..23fd9f2a --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Module.php @@ -0,0 +1,150 @@ +. + */ + +namespace DoctrineORMModule; + +use Zend\ModuleManager\Feature\AutoloaderProviderInterface; +use Zend\ModuleManager\Feature\ControllerProviderInterface; +use Zend\ModuleManager\Feature\BootstrapListenerInterface; +use Zend\ModuleManager\Feature\ServiceProviderInterface; +use Zend\ModuleManager\Feature\ConfigProviderInterface; +use Zend\ModuleManager\Feature\InitProviderInterface; +use Zend\ModuleManager\ModuleManagerInterface; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Loader\AutoloaderFactory; +use Zend\Loader\StandardAutoloader; +use Zend\EventManager\EventInterface; + +use Doctrine\ORM\Tools\Console\ConsoleRunner; +use Symfony\Component\Console\Helper\DialogHelper; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; +use Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand; +use Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand; +use Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand; +use Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand; +use Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand; +use Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand; + +/** + * Base module for Doctrine ORM. + * + * @license MIT + * @link www.doctrine-project.org + * @author Kyle Spraggs + * @author Marco Pivetta + */ +class Module implements + AutoloaderProviderInterface, + ControllerProviderInterface, + BootstrapListenerInterface, + ServiceProviderInterface, + ConfigProviderInterface, + InitProviderInterface +{ + /** + * {@inheritDoc} + */ + public function init(ModuleManagerInterface $manager) + { + $events = $manager->getEventManager(); + // Initialize logger collector once the profiler is initialized itself + $events->attach('profiler_init', function() use ($manager) { + $manager->getEvent()->getParam('ServiceManager')->get('doctrine.sql_logger_collector.orm_default'); + }); + } + + /** + * {@inheritDoc} + */ + public function getAutoloaderConfig() + { + return array( + AutoloaderFactory::STANDARD_AUTOLOADER => array( + StandardAutoloader::LOAD_NS => array( + __NAMESPACE__ => __DIR__, + ), + ), + ); + } + + /** + * {@inheritDoc} + */ + public function onBootstrap(EventInterface $e) + { + /* @var $app \Zend\Mvc\ApplicationInterface */ + $app = $e->getTarget(); + $events = $app->getEventManager()->getSharedManager(); + + // Attach to helper set event and load the entity manager helper. + $events->attach('doctrine', 'loadCli.post', function(EventInterface $e) { + /* @var $cli \Symfony\Component\Console\Application */ + $cli = $e->getTarget(); + + ConsoleRunner::addCommands($cli); + + if (class_exists('Doctrine\\DBAL\\Migrations\\Version')) { + $cli->addCommands(array( + new DiffCommand(), + new ExecuteCommand(), + new GenerateCommand(), + new MigrateCommand(), + new StatusCommand(), + new VersionCommand(), + )); + } + + /* @var $sm ServiceLocatorInterface */ + $sm = $e->getParam('ServiceManager'); + /* @var $em \Doctrine\ORM\EntityManager */ + $em = $sm->get('doctrine.entitymanager.orm_default'); + $helperSet = $cli->getHelperSet(); + $helperSet->set(new DialogHelper(), 'dialog'); + $helperSet->set(new ConnectionHelper($em->getConnection()), 'db'); + $helperSet->set(new EntityManagerHelper($em), 'em'); + }); + + $app->getServiceManager()->get('doctrine.entity_resolver.orm_default'); + } + + /** + * {@inheritDoc} + */ + public function getConfig() + { + return include __DIR__ . '/../../config/module.config.php'; + } + + /** + * {@inheritDoc} + */ + public function getServiceConfig() + { + return include __DIR__ . '/../../config/services.config.php'; + } + + /** + * {@inheritDoc} + */ + public function getControllerConfig() + { + return include __DIR__ . '/../../config/controllers.config.php'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/Configuration.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/Configuration.php new file mode 100644 index 00000000..3536aa62 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/Configuration.php @@ -0,0 +1,464 @@ + + * @author Marco Pivetta + */ +class Configuration extends DBALConfiguration +{ + /** + * Set the cache key for the metadata cache. Cache key + * is assembled as "doctrine.cache.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $metadataCache = 'array'; + + /** + * Set the cache key for the query cache. Cache key + * is assembled as "doctrine.cache.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $queryCache = 'array'; + + /** + * Set the cache key for the result cache. Cache key + * is assembled as "doctrine.cache.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $resultCache = 'array'; + + /** + * Set the driver key for the metadata driver. Driver key + * is assembeled as "doctrine.driver.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $driver = 'orm_default'; + + /** + * Automatic generation of proxies (disable for production!) + * + * @var bool + */ + protected $generateProxies = true; + + /** + * Proxy directory. + * + * @var string + */ + protected $proxyDir = 'data'; + + /** + * Proxy namespace. + * + * @var string + */ + protected $proxyNamespace = 'DoctrineORMModule\Proxy'; + + /** + * Entity alias map. + * + * @var array + */ + protected $entityNamespaces = array(); + + /** + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * @var array + */ + protected $datetimeFunctions = array(); + + /** + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * @var array + */ + protected $stringFunctions = array(); + + /** + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * @var array + */ + protected $numericFunctions = array(); + + /** + * Keys must be the name of the custom filter and the value must be + * the class name for the custom filter. + * + * @var array + */ + protected $filters = array(); + + /** + * Keys must be the name of the query and values the DQL query string. + * + * @var array + */ + protected $namedQueries = array(); + + /** + * Keys must be the name of the query and the value is an array containing + * the keys 'sql' for native SQL query string and 'rsm' for the Query\ResultSetMapping. + * + * @var array + */ + protected $namedNativeQueries = array(); + + /** + * Keys must be the name of the custom hydration method and the value must be + * the class name for the custom hydrator + * + * @var array + */ + protected $customHydrationModes = array(); + + /** + * Naming strategy or name of the naming strategy service to be set in ORM + * configuration (if any) + * + * @var string|null|NamingStrategy + */ + protected $namingStrategy; + + /** + * @param array $datetimeFunctions + * @return self + */ + public function setDatetimeFunctions($datetimeFunctions) + { + $this->datetimeFunctions = $datetimeFunctions; + + return $this; + } + + /** + * @return array + */ + public function getDatetimeFunctions() + { + return $this->datetimeFunctions; + } + + /** + * @param string $driver + * @return self + */ + public function setDriver($driver) + { + $this->driver = $driver; + + return $this; + } + + /** + * @return string + */ + public function getDriver() + { + return "doctrine.driver.{$this->driver}"; + } + + /** + * @param array $entityNamespaces + * @return self + */ + public function setEntityNamespaces($entityNamespaces) + { + $this->entityNamespaces = $entityNamespaces; + + return $this; + } + + /** + * @return array + */ + public function getEntityNamespaces() + { + return $this->entityNamespaces; + } + + /** + * @param boolean $generateProxies + * @return self + */ + public function setGenerateProxies($generateProxies) + { + $this->generateProxies = $generateProxies; + + return $this; + } + + /** + * @return boolean + */ + public function getGenerateProxies() + { + return $this->generateProxies; + } + + /** + * @param string $metadataCache + * @return self + */ + public function setMetadataCache($metadataCache) + { + $this->metadataCache = $metadataCache; + + return $this; + } + + /** + * @return string + */ + public function getMetadataCache() + { + return "doctrine.cache.{$this->metadataCache}"; + } + + /** + * @param string $resultCache + * @return self + */ + public function setResultCache($resultCache) + { + $this->resultCache = $resultCache; + + return $this; + } + + /** + * @return string + */ + public function getResultCache() + { + return "doctrine.cache.{$this->resultCache}"; + } + + /** + * @param array $namedNativeQueries + * @return self + */ + public function setNamedNativeQueries($namedNativeQueries) + { + $this->namedNativeQueries = $namedNativeQueries; + + return $this; + } + + /** + * @return array + */ + public function getNamedNativeQueries() + { + return $this->namedNativeQueries; + } + + /** + * @param array $namedQueries + * @return self + */ + public function setNamedQueries($namedQueries) + { + $this->namedQueries = $namedQueries; + + return $this; + } + + /** + * @return array + */ + public function getNamedQueries() + { + return $this->namedQueries; + } + + /** + * @param array $numericFunctions + * @return self + */ + public function setNumericFunctions($numericFunctions) + { + $this->numericFunctions = $numericFunctions; + + return $this; + } + + /** + * @return array + */ + public function getNumericFunctions() + { + return $this->numericFunctions; + } + + /** + * + * @param array $filters + * @return self + */ + public function setFilters($filters) + { + $this->filters = $filters; + + return $this; + } + + /** + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } + + /** + * @param string $proxyDir + * @return self + */ + public function setProxyDir($proxyDir) + { + $this->proxyDir = $proxyDir; + + return $this; + } + + /** + * @return string + */ + public function getProxyDir() + { + return $this->proxyDir; + } + + /** + * @param string $proxyNamespace + * @return self + */ + public function setProxyNamespace($proxyNamespace) + { + $this->proxyNamespace = $proxyNamespace; + + return $this; + } + + /** + * @return string + */ + public function getProxyNamespace() + { + return $this->proxyNamespace; + } + + /** + * @param string $queryCache + * @return self + */ + public function setQueryCache($queryCache) + { + $this->queryCache = $queryCache; + + return $this; + } + + /** + * @return string + */ + public function getQueryCache() + { + return "doctrine.cache.{$this->queryCache}"; + } + + /** + * @param array $stringFunctions + * @return self + */ + public function setStringFunctions($stringFunctions) + { + $this->stringFunctions = $stringFunctions; + + return $this; + } + + /** + * @return array + */ + public function getStringFunctions() + { + return $this->stringFunctions; + } + + /** + * @param array $modes + * @return self + */ + public function setCustomHydrationModes($modes) + { + $this->customHydrationModes = $modes; + + return $this; + } + + /** + * @return array + */ + public function getCustomHydrationModes() + { + return $this->customHydrationModes; + } + + /** + * @param string|null|NamingStrategy $namingStrategy + * @return self + * @throws InvalidArgumentException when the provided naming strategy does not fit the expected type + */ + public function setNamingStrategy($namingStrategy) + { + if ( + null === $namingStrategy + || is_string($namingStrategy) + || $namingStrategy instanceof NamingStrategy + ) { + $this->namingStrategy = $namingStrategy; + + return $this; + } + + throw new InvalidArgumentException(sprintf( + 'namingStrategy must be either a string, a Doctrine\ORM\Mapping\NamingStrategy ' + . 'instance or null, %s given', + is_object($namingStrategy) ? get_class($namingStrategy) : gettype($namingStrategy) + )); + } + + /** + * @return string|null|NamingStrategy + */ + public function getNamingStrategy() + { + return $this->namingStrategy; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConfiguration.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConfiguration.php new file mode 100644 index 00000000..f978689c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConfiguration.php @@ -0,0 +1,104 @@ +. + */ + +namespace DoctrineORMModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * Configuration options for a DBAL Connection + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class DBALConfiguration extends AbstractOptions +{ + /** + * Set the cache key for the result cache. Cache key + * is assembled as "doctrine.cache.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $resultCache = 'array'; + + /** + * Set the class name of the SQL Logger, or null, to disable. + * + * @var string + */ + protected $sqlLogger = null; + + /** + * Keys must be the name of the type identifier and value is + * the class name of the Type + * + * @var array + */ + protected $types = array(); + + /** + * @param string $resultCache + */ + public function setResultCache($resultCache) + { + $this->resultCache = $resultCache; + } + + /** + * @return string + */ + public function getResultCache() + { + return 'doctrine.cache.' . $this->resultCache; + } + + /** + * @param string $sqlLogger + */ + public function setSqlLogger($sqlLogger) + { + $this->sqlLogger = $sqlLogger; + } + + /** + * @return string + */ + public function getSqlLogger() + { + return $this->sqlLogger; + } + + /** + * @param array $types + */ + public function setTypes(array $types) + { + $this->types = $types; + } + + /** + * @return string + */ + public function getTypes() + { + return $this->types; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConnection.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConnection.php new file mode 100644 index 00000000..0c992e39 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/DBALConnection.php @@ -0,0 +1,202 @@ +. + */ + +namespace DoctrineORMModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * DBAL Connection options + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class DBALConnection extends AbstractOptions +{ + /** + * Set the configuration key for the Configuration. Configuration key + * is assembled as "doctrine.configuration.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $configuration = 'orm_default'; + + /** + * Set the eventmanager key for the EventManager. EventManager key + * is assembled as "doctrine.eventmanager.{key}" and pulled from + * service locator. + * + * @var string + */ + protected $eventmanager = 'orm_default'; + + /** + * Set the PDO instance, if any, to use. If a string is set + * then the alias is pulled from the service locator. + * + * @var null|string|\PDO + */ + protected $pdo = null; + + /** + * Setting the driver is deprecated. You should set the + * driver class directly instead. + * + * @var string + */ + protected $driverClass = 'Doctrine\DBAL\Driver\PDOMySql\Driver'; + + /** + * Set the wrapper class for the driver. In general, this shouldn't + * need to be changed. + * + * @var string|null + */ + protected $wrapperClass = null; + + /** + * Driver specific connection parameters. + * + * @var array + */ + protected $params = array(); + + /** + * @var array + */ + protected $doctrineTypeMappings = array(); + + /** + * @param string $configuration + */ + public function setConfiguration($configuration) + { + $this->configuration = $configuration; + } + + /** + * @return string + */ + public function getConfiguration() + { + return "doctrine.configuration.{$this->configuration}"; + } + + /** + * @param string $eventmanager + */ + public function setEventmanager($eventmanager) + { + $this->eventmanager = $eventmanager; + } + + /** + * @return string + */ + public function getEventmanager() + { + return "doctrine.eventmanager.{$this->eventmanager}"; + } + + /** + * @param array $params + */ + public function setParams($params) + { + $this->params = $params; + } + + /** + * @return array + */ + public function getParams() + { + return $this->params; + } + + /** + * @param array $doctrineTypeMappings + * @return \DoctrineORMModule\Options\DBALConnection + */ + public function setDoctrineTypeMappings($doctrineTypeMappings) + { + $this->doctrineTypeMappings = (array) $doctrineTypeMappings; + + return $this; + } + + /** + * + * @return array + */ + public function getDoctrineTypeMappings() + { + return $this->doctrineTypeMappings; + } + + /** + * @param null|string $driverClass + */ + public function setDriverClass($driverClass) + { + $this->driverClass = $driverClass; + } + + /** + * @return null|string + */ + public function getDriverClass() + { + return $this->driverClass; + } + + /** + * @param null|\PDO|string $pdo + */ + public function setPdo($pdo) + { + $this->pdo = $pdo; + } + + /** + * @return null|\PDO|string + */ + public function getPdo() + { + return $this->pdo; + } + + /** + * @param string $wrapperClass + */ + public function setWrapperClass($wrapperClass) + { + $this->wrapperClass = $wrapperClass; + } + + /** + * @return string + */ + public function getWrapperClass() + { + return $this->wrapperClass; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityManager.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityManager.php new file mode 100644 index 00000000..1449abb5 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityManager.php @@ -0,0 +1,65 @@ +configuration = $configuration; + + return $this; + } + + /** + * @return string + */ + public function getConfiguration() + { + return "doctrine.configuration.{$this->configuration}"; + } + + /** + * @param string $connection + * @return self + */ + public function setConnection($connection) + { + $this->connection = $connection; + + return $this; + } + + /** + * @return string + * @return self + */ + public function getConnection() + { + return 'doctrine.connection.' . $this->connection; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityResolver.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityResolver.php new file mode 100644 index 00000000..a67da413 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/EntityResolver.php @@ -0,0 +1,73 @@ +eventManager = $eventManager; + + return $this; + } + + /** + * @return string + */ + public function getEventManager() + { + return "doctrine.eventmanager.{$this->eventManager}"; + } + + /** + * @param array $resolvers + * @throws InvalidArgumentException + */ + public function setResolvers(array $resolvers) + { + foreach ($resolvers as $old => $new) { + if (!class_exists($new)) { + throw new InvalidArgumentException(sprintf( + '%s is resolved to the entity %s, which does not exist', + $old, + $new + ) + ); + } + + $this->resolvers[$old] = $new; + } + } + + /** + * @return array + */ + public function getResolvers() + { + return $this->resolvers; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/SQLLoggerCollectorOptions.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/SQLLoggerCollectorOptions.php new file mode 100644 index 00000000..5e9e5f71 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Options/SQLLoggerCollectorOptions.php @@ -0,0 +1,101 @@ +. + */ + +namespace DoctrineORMModule\Options; + +use Zend\Stdlib\AbstractOptions; + +/** + * Configuration options for an collector + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class SQLLoggerCollectorOptions extends AbstractOptions +{ + /** + * @var string name to be assigned to the collector + */ + protected $name = 'orm_default'; + + /** + * @var string|null service name of the configuration where the logger has to be put + */ + protected $configuration; + + /** + * @var string|null service name of the SQLLogger to be used + */ + protected $sqlLogger; + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = (string) $name; + } + + /** + * Name of the collector + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string|null $configuration + */ + public function setConfiguration($configuration) + { + $this->configuration = $configuration ? (string) $configuration : null; + } + + /** + * Configuration service name (where to set the logger) + * + * @return string + */ + public function getConfiguration() + { + return $this->configuration ? $this->configuration : 'doctrine.configuration.orm_default'; + } + + /** + * @param string|null $sqlLogger + */ + public function setSqlLogger($sqlLogger) + { + $this->sqlLogger = $sqlLogger ? (string) $sqlLogger : null; + } + + /** + * SQLLogger service name + * + * @return string|null + */ + public function getSqlLogger() + { + return $this->sqlLogger; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Paginator/Adapter/DoctrinePaginator.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Paginator/Adapter/DoctrinePaginator.php new file mode 100644 index 00000000..5ce7a874 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Paginator/Adapter/DoctrinePaginator.php @@ -0,0 +1,87 @@ +. + */ + +namespace DoctrineORMModule\Paginator\Adapter; + +use Doctrine\ORM\Tools\Pagination\Paginator; +use Zend\Paginator\Adapter\AdapterInterface; + +/** + * Paginator adapter for the Zend\Paginator component + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Tõnis Tobre + */ +class DoctrinePaginator implements AdapterInterface +{ + /** + * @var Paginator + */ + protected $paginator; + + /** + * Constructor + * + * @param Paginator $paginator + */ + public function __construct(Paginator $paginator) + { + $this->paginator = $paginator; + } + + /** + * @param Paginator $paginator + * @return self + */ + public function setPaginator(Paginator $paginator) + { + $this->paginator = $paginator; + } + + /** + * @return Paginator + */ + public function getPaginator() + { + return $this->paginator; + } + + /** + * {@inheritDoc} + */ + public function getItems($offset, $itemCountPerPage) + { + $this->paginator + ->getQuery() + ->setFirstResult($offset) + ->setMaxResults($itemCountPerPage); + + return $this->paginator->getIterator(); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return $this->paginator->count(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/ConfigurationFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/ConfigurationFactory.php new file mode 100644 index 00000000..b233801f --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/ConfigurationFactory.php @@ -0,0 +1,90 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use DoctrineORMModule\Service\DBALConfigurationFactory as DoctrineConfigurationFactory; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\Exception\InvalidArgumentException; +use Doctrine\ORM\Configuration; + +class ConfigurationFactory extends DoctrineConfigurationFactory +{ + public function createService(ServiceLocatorInterface $serviceLocator) + { + /** @var $options \DoctrineORMModule\Options\Configuration */ + $options = $this->getOptions($serviceLocator); + $config = new Configuration(); + + $config->setAutoGenerateProxyClasses($options->getGenerateProxies()); + $config->setProxyDir($options->getProxyDir()); + $config->setProxyNamespace($options->getProxyNamespace()); + + $config->setEntityNamespaces($options->getEntityNamespaces()); + + $config->setCustomDatetimeFunctions($options->getDatetimeFunctions()); + $config->setCustomStringFunctions($options->getStringFunctions()); + $config->setCustomNumericFunctions($options->getNumericFunctions()); + + foreach ($options->getNamedQueries() as $name => $query) { + $config->addNamedQuery($name, $query); + } + + foreach ($options->getNamedNativeQueries() as $name => $query) { + $config->addNamedNativeQuery($name, $query['sql'], new $query['rsm']); + } + + foreach ($options->getCustomHydrationModes() AS $modeName => $hydrator) { + $config->addCustomHydrationMode($modeName, $hydrator); + } + + foreach ($options->getFilters() as $name => $class) { + $config->addFilter($name, $class); + } + + $config->setMetadataCacheImpl($serviceLocator->get($options->getMetadataCache())); + $config->setQueryCacheImpl($serviceLocator->get($options->getQueryCache())); + $config->setResultCacheImpl($serviceLocator->get($options->getResultCache())); + $config->setMetadataDriverImpl($serviceLocator->get($options->getDriver())); + + if ($namingStrategy = $options->getNamingStrategy()) { + if (is_string($namingStrategy)) { + if (!$serviceLocator->has($namingStrategy)) { + throw new InvalidArgumentException(sprintf( + 'Naming strategy "%s" not found', + $namingStrategy + )); + } + + $config->setNamingStrategy($serviceLocator->get($namingStrategy)); + } else { + $config->setNamingStrategy($namingStrategy); + } + } + + $this->setupDBALConfiguration($serviceLocator, $config); + + return $config; + } + + protected function getOptionsClass() + { + return 'DoctrineORMModule\Options\Configuration'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConfigurationFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConfigurationFactory.php new file mode 100644 index 00000000..d68ddae7 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConfigurationFactory.php @@ -0,0 +1,116 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use RuntimeException; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Types\Type; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * DBAL Configuration ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class DBALConfigurationFactory implements FactoryInterface +{ + /** + * @var string + */ + protected $name; + + /** + * @param $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * {@inheritDoc} + * @return \Doctrine\DBAL\Configuration + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + $config = new Configuration(); + $this->setupDBALConfiguration($serviceLocator, $config); + + return $config; + } + + /** + * @param ServiceLocatorInterface $serviceLocator + * @param Configuration $config + */ + public function setupDBALConfiguration(ServiceLocatorInterface $serviceLocator, Configuration $config) + { + $options = $this->getOptions($serviceLocator); + $config->setResultCacheImpl($serviceLocator->get($options->resultCache)); + + $sqlLogger = $options->sqlLogger; + if (is_string($sqlLogger) and $serviceLocator->has($sqlLogger)) { + $sqlLogger = $serviceLocator->get($sqlLogger); + } + $config->setSQLLogger($sqlLogger); + + foreach ($options->types as $name => $class) { + if (Type::hasType($name)) { + Type::overrideType($name, $class); + } else { + Type::addType($name, $class); + } + } + } + + /** + * @param ServiceLocatorInterface $serviceLocator + * @return mixed + * @throws RuntimeException + */ + public function getOptions(ServiceLocatorInterface $serviceLocator) + { + $options = $serviceLocator->get('Config'); + $options = $options['doctrine']; + $options = isset($options['configuration'][$this->name]) ? $options['configuration'][$this->name] : null; + + if (null === $options) { + throw new RuntimeException(sprintf( + 'Configuration with name "%s" could not be found in "doctrine.configuration".', + $this->name + )); + } + + $optionsClass = $this->getOptionsClass(); + + return new $optionsClass($options); + } + + /** + * @return string + */ + protected function getOptionsClass() + { + return 'DoctrineModule\Options\Configuration'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConnectionFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConnectionFactory.php new file mode 100644 index 00000000..743d83b3 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/DBALConnectionFactory.php @@ -0,0 +1,77 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use Doctrine\DBAL\DriverManager; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * DBAL Connection ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Kyle Spraggs + */ +class DBALConnectionFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + * @return \Doctrine\DBAL\Connection + */ + public function createService(ServiceLocatorInterface $sl) + { + /** @var $options \DoctrineORMModule\Options\DBALConnection */ + $options = $this->getOptions($sl, 'connection'); + $pdo = $options->getPdo(); + + if (is_string($pdo)) { + $pdo = $sl->get($pdo); + } + + $params = array( + 'driverClass' => $options->getDriverClass(), + 'wrapperClass' => $options->getWrapperClass(), + 'pdo' => $pdo, + ); + $params = array_merge($params, $options->getParams()); + + $configuration = $sl->get($options->getConfiguration()); + $eventManager = $sl->get($options->getEventManager()); + + $connection = DriverManager::getConnection($params, $configuration, $eventManager); + $platform = $connection->getDatabasePlatform(); + foreach ($options->getDoctrineTypeMappings() as $dbType => $doctrineType) { + $platform->registerDoctrineTypeMapping($dbType, $doctrineType); + } + + return $connection; + } + + /** + * Get the class name of the options associated with this factory. + * + * @return string + */ + public function getOptionsClass() + { + return 'DoctrineORMModule\Options\DBALConnection'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityManagerFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityManagerFactory.php new file mode 100644 index 00000000..f76478a0 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityManagerFactory.php @@ -0,0 +1,49 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use Doctrine\ORM\EntityManager; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +class EntityManagerFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + * @return EntityManager + */ + public function createService(ServiceLocatorInterface $sl) + { + /* @var $options \DoctrineORMModule\Options\EntityManager */ + $options = $this->getOptions($sl, 'entitymanager'); + $connection = $sl->get($options->getConnection()); + $config = $sl->get($options->getConfiguration()); + + return EntityManager::create($connection, $config); + } + + /** + * {@inheritDoc} + */ + public function getOptionsClass() + { + return 'DoctrineORMModule\Options\EntityManager'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityResolverFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityResolverFactory.php new file mode 100644 index 00000000..d2522e9b --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/EntityResolverFactory.php @@ -0,0 +1,59 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use Doctrine\ORM\Events; +use Doctrine\ORM\Tools\ResolveTargetEntityListener; +use DoctrineModule\Service\AbstractFactory; +use Zend\ServiceManager\ServiceLocatorInterface; + +class EntityResolverFactory extends AbstractFactory +{ + /** + * {@inheritDoc} + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + /* @var $options \DoctrineORMModule\Options\EntityResolver */ + $options = $this->getOptions($serviceLocator, 'entity_resolver'); + $eventManager = $serviceLocator->get($options->getEventManager()); + $resolvers = $options->getResolvers(); + + $targetEntityListener = new ResolveTargetEntityListener(); + + foreach ($resolvers as $oldEntity => $newEntity) { + $targetEntityListener->addResolveTargetEntity($oldEntity, $newEntity, array()); + } + + $eventManager->addEventListener(Events::loadClassMetadata, $targetEntityListener); + + return $eventManager; + } + + /** + * Get the class name of the options associated with this factory. + * + * @return string + */ + public function getOptionsClass() + { + return 'DoctrineORMModule\Options\EntityResolver'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/SQLLoggerCollectorFactory.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/SQLLoggerCollectorFactory.php new file mode 100644 index 00000000..358baa08 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Service/SQLLoggerCollectorFactory.php @@ -0,0 +1,116 @@ +. + */ + +namespace DoctrineORMModule\Service; + +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +use RuntimeException; + +use DoctrineORMModule\Collector\SQLLoggerCollector; + +use Doctrine\DBAL\Logging\DebugStack; +use Doctrine\DBAL\Logging\LoggerChain; + +/** + * DBAL Configuration ServiceManager factory + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class SQLLoggerCollectorFactory implements FactoryInterface +{ + /** + * @var string + */ + protected $name; + + /** + * @param $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * {@inheritDoc} + */ + public function createService(ServiceLocatorInterface $serviceLocator) + { + /** @var $options \DoctrineORMModule\Options\SQLLoggerCollectorOptions */ + $options = $this->getOptions($serviceLocator); + + // @todo always ask the serviceLocator instead? (add a factory?) + if ($options->getSqlLogger()) { + $debugStackLogger = $serviceLocator->get($options->getSqlLogger()); + } else { + $debugStackLogger = new DebugStack(); + } + + /* @var $configuration \Doctrine\ORM\Configuration */ + $configuration = $serviceLocator->get($options->getConfiguration()); + + if (null !== $configuration->getSQLLogger()) { + $logger = new LoggerChain(); + $logger->addLogger($debugStackLogger); + $logger->addLogger($configuration->getSQLLogger()); + $configuration->setSQLLogger($logger); + } else { + $configuration->setSQLLogger($debugStackLogger); + } + + return new SQLLoggerCollector($debugStackLogger, $options->getName()); + } + + /** + * @param ServiceLocatorInterface $serviceLocator + * @return mixed + * @throws RuntimeException + */ + protected function getOptions(ServiceLocatorInterface $serviceLocator) + { + $options = $serviceLocator->get('Config'); + $options = $options['doctrine']; + $options = isset($options['sql_logger_collector'][$this->name]) + ? $options['sql_logger_collector'][$this->name] + : null; + + if (null === $options) { + throw new RuntimeException(sprintf( + 'Configuration with name "%s" could not be found in "doctrine.sql_logger_collector".', + $this->name + )); + } + + $optionsClass = $this->getOptionsClass(); + + return new $optionsClass($options); + } + + /** + * {@inheritDoc} + */ + protected function getOptionsClass() + { + return 'DoctrineORMModule\Options\SQLLoggerCollectorOptions'; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Stdlib/Hydrator/DoctrineEntity.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Stdlib/Hydrator/DoctrineEntity.php new file mode 100644 index 00000000..869dfcc6 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Stdlib/Hydrator/DoctrineEntity.php @@ -0,0 +1,37 @@ +. + */ + +namespace DoctrineORMModule\Stdlib\Hydrator; + +use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineObjectHydrator; + +/** + * This hydrator is used as an optimization purpose for Doctrine ORM, and retrieves references to + * objects instead of fetching the object from the database. + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.5.0 + * @author Michaël Gallego + * + * @deprecated will be removed in 0.8.0, use DoctrineModule\Stdlib\Hydrator\DoctrineObject instead + */ +class DoctrineEntity extends DoctrineObjectHydrator +{ +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Version.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Version.php new file mode 100644 index 00000000..a940680a --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Version.php @@ -0,0 +1,33 @@ +. + */ + +namespace DoctrineORMModule; + +/** + * Version + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @since 0.1.0 + * @author Kyle Spraggs + */ +class Version +{ + const VERSION = '0.4.0'; +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/MetadataGrapher.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/MetadataGrapher.php new file mode 100644 index 00000000..08b3dede --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/MetadataGrapher.php @@ -0,0 +1,248 @@ +. + */ + +namespace DoctrineORMModule\Yuml; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Utility to generate Yuml compatible strings from metadata graphs + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class MetadataGrapher +{ + /** + * Temporary array where already visited collections are stored + * + * @var array + */ + protected $visitedAssociations = array(); + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata[] + */ + private $metadata; + + /** + * Generate a YUML compatible `dsl_text` to describe a given array + * of entities + * + * @param $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata[] + * + * @return string + */ + public function generateFromMetadata(array $metadata) + { + $this->metadata = $metadata; + $this->visitedAssociations = array(); + $str = array(); + + foreach ($metadata as $class) { + $parent = $this->getParent($class); + + if ($parent) { + $str[] = $this->getClassString($parent) . '^' . $this->getClassString($class); + } + + $associations = $class->getAssociationNames(); + + if (empty($associations) && !isset($this->visitedAssociations[$class->getName()])) { + $str[] = $this->getClassString($class); + + continue; + } + + foreach ($associations as $associationName) { + if ($parent && in_array($associationName, $parent->getAssociationNames())) { + continue; + } + + if ($this->visitAssociation($class->getName(), $associationName)) { + $str[] = $this->getAssociationString($class, $associationName); + } + } + } + + return implode(',', $str); + } + + private function getAssociationString(ClassMetadata $class1, $association) + { + $targetClassName = $class1->getAssociationTargetClass($association); + $class2 = $this->getClassByName($targetClassName); + $isInverse = $class1->isAssociationInverseSide($association); + $class1Count = $class1->isCollectionValuedAssociation($association) ? 2 : 1; + + if (null === $class2) { + return $this->getClassString($class1) + . ($isInverse ? '<' : '<>') . '-' . $association . ' ' + . ($class1Count > 1 ? '*' : ($class1Count ? '1' : '')) + . ($isInverse ? '<>' : '>') + . '[' . str_replace('\\', '.', $targetClassName) . ']'; + } + + $class1SideName = $association; + $class2SideName = ''; + $class2Count = 0; + $bidirectional = false; + + if ($isInverse) { + $class2SideName = (string) $class1->getAssociationMappedByTargetField($association); + + if ($class2SideName) { + $class2Count = $class2->isCollectionValuedAssociation($class2SideName) ? 2 : 1; + $bidirectional = true; + } + } else { + foreach ($class2->getAssociationNames() as $class2Side) { + if ( + $class2->isAssociationInverseSide($class2Side) + && ($class2->getAssociationMappedByTargetField($class2Side) === $association) + ) { + $class2SideName = $class2Side; + $class2Count = $class2->isCollectionValuedAssociation($class2SideName) ? 2 : 1; + $bidirectional = true; + break; + } + } + } + + $this->visitAssociation($targetClassName, $class2SideName); + + return $this->getClassString($class1) + . ($bidirectional ? ($isInverse ? '<' : '<>') : '') // class2 side arrow + . ($class2SideName ? $class2SideName . ' ' : '') + . ($class2Count > 1 ? '*' : ($class2Count ? '1' : '')) // class2 side single/multi valued + . '-' + . $class1SideName . ' ' + . ($class1Count > 1 ? '*' : ($class1Count ? '1' : '')) // class1 side single/multi valued + . (($bidirectional && $isInverse) ? '<>' : '>') // class1 side arrow + . $this->getClassString($class2); + } + + /** + * Build the string representing the single graph item + * + * @param ClassMetadata $class + * + * @return string + */ + private function getClassString(ClassMetadata $class) + { + $this->visitAssociation($class->getName()); + + $className = $class->getName(); + $classText = '[' . str_replace('\\', '.', $className); + $fields = array(); + $parent = $this->getParent($class); + $parentFields = $parent ? $parent->getFieldNames() : array(); + + foreach ($class->getFieldNames() as $fieldName) { + if (in_array($fieldName, $parentFields)) { + continue; + } + + if ($class->isIdentifier($fieldName)) { + $fields[] = '+' . $fieldName; + } else { + $fields[] = $fieldName; + } + } + + if (!empty($fields)) { + $classText .= '|' . implode(';', $fields); + } + + $classText .= ']'; + + return $classText; + } + + /** + * Retrieve a class metadata instance by name from the given array + * + * @param string $className + * + * @return ClassMetadata|null + */ + private function getClassByName($className) + { + foreach ($this->metadata as $class) { + if ($class->getName() === $className) { + return $class; + } + } + + return null; + } + + /** + * Retrieve a class metadata's parent class metadata + * + * @param ClassMetadata $class + * + * @return ClassMetadata|null + */ + private function getParent($class) + { + $className = $class->getName(); + + if (!class_exists($className) || (!$parent = get_parent_class($className))) { + return null; + } + + return $this->getClassByName($parent); + } + + /** + * Visit a given association and mark it as visited + * + * @param string $className + * @param string|null $association + * + * @return bool true if the association was visited before + */ + private function visitAssociation($className, $association = null) + { + if (null === $association) { + if (isset($this->visitedAssociations[$className])) { + return false; + } + + $this->visitedAssociations[$className] = array(); + + return true; + } + + if (isset($this->visitedAssociations[$className][$association])) { + return false; + } + + if (!isset($this->visitedAssociations[$className])) { + $this->visitedAssociations[$className] = array(); + } + + $this->visitedAssociations[$className][$association] = true; + + return true; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/YumlController.php b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/YumlController.php new file mode 100644 index 00000000..7f92959c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/src/DoctrineORMModule/Yuml/YumlController.php @@ -0,0 +1,72 @@ +. + */ + +namespace DoctrineORMModule\Yuml; + +use Zend\Mvc\Controller\AbstractActionController; +use Zend\Http\Client; +use Zend\Http\Request; + +/** + * Utility to generate Yuml compatible strings from metadata graphs + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class YumlController extends AbstractActionController +{ + /** + * @var Client + */ + protected $httpClient; + + /** + * @param Client $httpClient + */ + public function __construct(Client $httpClient) + { + $this->httpClient = $httpClient; + } + + /** + * Redirects the user to a YUML graph drawn with the provided `dsl_text` + * + * @return \Zend\Http\Response + * + * @throws \UnexpectedValueException if the YUML service answered incorrectly + */ + public function indexAction() + { + /* @var $request \Zend\Http\Request */ + $request = $this->getRequest(); + $this->httpClient->setMethod(Request::METHOD_POST); + $this->httpClient->setParameterPost(array('dsl_text' => $request->getPost('dsl_text'))); + $response = $this->httpClient->send(); + + if (!$response->isSuccess()) { + throw new \UnexpectedValueException('HTTP Request failed'); + } + + /* @var $redirect \Zend\Mvc\Controller\Plugin\Redirect */ + $redirect = $this->plugin('redirect'); + + return $redirect->toUrl('http://yuml.me/' . $response->getBody()); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/.gitignore b/vendor/doctrine/doctrine-orm-module/tests/.gitignore new file mode 100644 index 00000000..ba33e471 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/.gitignore @@ -0,0 +1 @@ +TestConfiguration.php diff --git a/vendor/doctrine/doctrine-orm-module/tests/Bootstrap.php b/vendor/doctrine/doctrine-orm-module/tests/Bootstrap.php new file mode 100644 index 00000000..6122c91d --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/Bootstrap.php @@ -0,0 +1,34 @@ +. + */ + +if ( + !($loader = @include __DIR__ . '/../vendor/autoload.php') + && !($loader = @include __DIR__ . '/../../../autoload.php') +) { + throw new RuntimeException('vendor/autoload.php could not be found. Did you run `php composer.phar install`?'); +} + +/* @var $loader \Composer\Autoload\ClassLoader */ +$loader->add('DoctrineORMModuleTest\\', __DIR__); + +if (!$config = @include __DIR__ . '/TestConfiguration.php') { + $config = require __DIR__ . '/TestConfiguration.php.dist'; +} + +\DoctrineORMModuleTest\Util\ServiceManagerFactory::setConfig($config); diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Category.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Category.php new file mode 100644 index 00000000..ae885f98 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Category.php @@ -0,0 +1,44 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/City.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/City.php new file mode 100644 index 00000000..0139ac09 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/City.php @@ -0,0 +1,61 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getName() + { + return $this->name; + } + + public function setCountry(Country $country) + { + $this->country = $country; + + return $this; + } + + public function getCountry() + { + return $this->country; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Country.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Country.php new file mode 100644 index 00000000..cf383b2f --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Country.php @@ -0,0 +1,44 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Date.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Date.php new file mode 100644 index 00000000..0eb0553e --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Date.php @@ -0,0 +1,48 @@ +id; + } + + /** + * @param \DateTime $date + */ + public function setDate($date) + { + $this->date = $date; + } + + /** + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Product.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Product.php new file mode 100644 index 00000000..6ba02188 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/Product.php @@ -0,0 +1,61 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getName() + { + return $this->name; + } + + public function setCategories($categories) + { + $this->categories = $categories; + + return $this; + } + + public function getCategories() + { + return $this->categories; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/ResolveTarget.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/ResolveTarget.php new file mode 100644 index 00000000..b166b23a --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Entity/ResolveTarget.php @@ -0,0 +1,23 @@ +id; + } + + /** + * @param string $password + */ + public function setPassword($password) + { + $this->password = (string) $password; + } + + /** + * @return string|null + */ + public function getPassword() + { + return $this->password; + } + + /** + * @param string $username + */ + public function setUsername($username) + { + $this->username = (string) $username; + } + + /** + * @return string|null + */ + public function getUsername() + { + return $this->username; + } + + /** + * Used for testing DoctrineEntity form element + * + * @return string + */ + public function __toString() + { + return (string) $this->username; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Fixture/TestFixture.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Fixture/TestFixture.php new file mode 100644 index 00000000..9d9f575c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/Fixture/TestFixture.php @@ -0,0 +1,65 @@ +. + */ + +namespace DoctrineORMModuleTest\Assets\Fixture; + +use Doctrine\Common\DataFixtures\AbstractFixture; +use Doctrine\Common\Persistence\ObjectManager; + +use DoctrineORMModuleTest\Assets\Entity\Test as TestEntity; +use DoctrineORMModuleTest\Assets\Entity\Category; +use DoctrineORMModuleTest\Assets\Entity\Country; + +/** + * Fixture that loads a constant amount of \DoctrineORMModuleTest\Assets\Entity\Test objects into the manager + */ +class TestFixture extends AbstractFixture +{ + /** + * Number of instances to build when the fixture is loaded + */ + const INSTANCES_COUNT = 100; + + /** + * {@inheritDoc} + */ + public function load(ObjectManager $manager) + { + for ($i = 0; $i < self::INSTANCES_COUNT; $i += 1) { + $instance = new TestEntity(); + $instance->setUsername('username'); + $instance->setPassword('password'); + $manager->persist($instance); + } + + for ($i = 0 ; $i < self::INSTANCES_COUNT ; $i += 1) { + $instance = new Category(); + $instance->setName('category'); + $manager->persist($instance); + } + + for ($i = 0 ; $i < self::INSTANCES_COUNT ; $i += 1) { + $instance = new Country(); + $instance->setName('country'); + $manager->persist($instance); + } + + $manager->flush(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Address.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Address.php new file mode 100644 index 00000000..e9a753b9 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Address.php @@ -0,0 +1,51 @@ +. + */ + +namespace DoctrineORMModuleTest\Assets\GraphEntity; + +use Doctrine\ORM\Mapping as ORM; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * Part of the test assets used to produce a demo of graphs in the ZDT integration + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + * + * @ORM\Entity() + */ +class Address +{ + /** + * @var int + * @ORM\Id() + * @ORM\GeneratedValue(strategy="AUTO") + * @ORM\Column(type="integer") + */ + protected $id; + + /** + * + */ + public function __construct() + { + $this->groups = new ArrayCollection(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Session.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Session.php new file mode 100644 index 00000000..103c3295 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/Session.php @@ -0,0 +1,48 @@ +. + */ + +namespace DoctrineORMModuleTest\Assets\GraphEntity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Part of the test assets used to produce a demo of graphs in the ZDT integration + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + * + * @ORM\Entity() + */ +class Session +{ + /** + * @var int + * @ORM\Id() + * @ORM\GeneratedValue(strategy="AUTO") + * @ORM\Column(type="integer") + */ + protected $id; + + /** + * @var User + * @ORM\ManyToOne(targetEntity="User", inversedBy="address") + */ + protected $user; +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/User.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/User.php new file mode 100644 index 00000000..550e9b31 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/User.php @@ -0,0 +1,70 @@ +. + */ + +namespace DoctrineORMModuleTest\Assets\GraphEntity; + +use Doctrine\ORM\Mapping as ORM; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * Part of the test assets used to produce a demo of graphs in the ZDT integration + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + * + * @ORM\Entity() + */ +class User +{ + /** + * @var int + * @ORM\Id() + * @ORM\GeneratedValue(strategy="AUTO") + * @ORM\Column(type="integer") + */ + protected $id; + + /** + * @var \Doctrine\Common\Collections\Collection|UserGroup[] + * @ORM\ManyToMany(targetEntity="UserGroup", mappedBy="users") + */ + protected $groups; + + /** + * @var \Doctrine\Common\Collections\Collection|Session[] + * @ORM\OneToMany(targetEntity="Session", mappedBy="user") + */ + protected $sessions; + + /** + * @var Address + * @ORM\OneToOne(targetEntity="Address") + */ + protected $address; + + /** + * + */ + public function __construct() + { + $this->groups = new ArrayCollection(); + $this->sessions = new ArrayCollection(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/UserGroup.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/UserGroup.php new file mode 100644 index 00000000..3830a01d --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Assets/GraphEntity/UserGroup.php @@ -0,0 +1,55 @@ +. + */ + +namespace DoctrineORMModuleTest\Assets\GraphEntity; + +use Doctrine\ORM\Mapping as ORM; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * Part of the test assets used to produce a demo of graphs in the ZDT integration + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + * + * @ORM\Entity() + */ +class UserGroup +{ + /** + * @var int + * @ORM\Id() + * @ORM\GeneratedValue(strategy="AUTO") + * @ORM\Column(type="integer") + */ + protected $id; + + /** + * @var \Doctrine\Common\Collections\Collection|User[] + * @ORM\ManyToMany(targetEntity="User", inversedBy="groups") + */ + protected $users; + + /* */ + public function __construct() + { + $this->users = new ArrayCollection(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/CliTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/CliTest.php new file mode 100644 index 00000000..bb7673a6 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/CliTest.php @@ -0,0 +1,143 @@ +. + */ + +namespace DoctrineORMModuleTest; + +use PHPUnit_Framework_TestCase; +use DoctrineORMModuleTest\Util\ServiceManagerFactory; +use Zend\EventManager\EventInterface; + +/** + * Tests used to verify that command line functionality is active + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class CliTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Console\Application + */ + protected $cli; + + /** + * @var \Doctrine\ORM\EntityManager + */ + protected $entityManager; + + /** + * {@inheritDoc} + */ + public function setUp() + { + $serviceManager = ServiceManagerFactory::getServiceManager(); + /* @var $sharedEventManager \Zend\EventManager\SharedEventManagerInterface */ + $sharedEventManager = $serviceManager->get('SharedEventManager'); + /* @var $application \Zend\Mvc\Application */ + $application = $serviceManager->get('Application'); + $invocations = 0; + + $sharedEventManager->attach('doctrine', 'loadCli.post', function() use (&$invocations) { + $invocations += 1; + }); + + $application->bootstrap(); + $this->entityManager = $serviceManager->get('doctrine.entitymanager.orm_default'); + $this->cli = $serviceManager->get('doctrine.cli'); + $this->assertSame(1, $invocations); + } + + public function testValidHelpers() + { + $helperSet = $this->cli->getHelperSet(); + + /* @var $emHelper \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper */ + $emHelper = $helperSet->get('em'); + $this->assertInstanceOf('Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper', $emHelper); + $this->assertSame($this->entityManager, $emHelper->getEntityManager()); + + /* @var $dbHelper \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper */ + $dbHelper = $helperSet->get('db'); + $this->assertInstanceOf('Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper', $dbHelper); + $this->assertSame($this->entityManager->getConnection(), $dbHelper->getConnection()); + } + + public function testValidCommands() + { + $this->assertInstanceOf('Doctrine\DBAL\Tools\Console\Command\ImportCommand', $this->cli->get('dbal:import')); + $this->assertInstanceOf('Doctrine\DBAL\Tools\Console\Command\RunSqlCommand', $this->cli->get('dbal:run-sql')); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand', + $this->cli->get('orm:clear-cache:metadata') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand', + $this->cli->get('orm:clear-cache:query') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand', + $this->cli->get('orm:clear-cache:result') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand', + $this->cli->get('orm:generate-proxies') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand', + $this->cli->get('orm:ensure-production-settings') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\InfoCommand', + $this->cli->get('orm:info') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand', + $this->cli->get('orm:schema-tool:create') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand', + $this->cli->get('orm:schema-tool:update') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand', + $this->cli->get('orm:schema-tool:drop') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand', + $this->cli->get('orm:validate-schema') + ); + $this->assertInstanceOf( + 'Doctrine\ORM\Tools\Console\Command\RunDqlCommand', + $this->cli->get('orm:run-dql') + ); + $this->assertInstanceOf( + 'Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand', + $this->cli->get('migrations:generate') + ); + $this->assertInstanceOf( + 'Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand', + $this->cli->get('migrations:diff') + ); + $this->assertInstanceOf( + 'Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand', + $this->cli->get('migrations:execute') + ); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/MappingCollectorTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/MappingCollectorTest.php new file mode 100644 index 00000000..217d67f9 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/MappingCollectorTest.php @@ -0,0 +1,138 @@ +. + */ + +namespace DoctrineORMModuleTest\Collector; + +use PHPUnit_Framework_TestCase; +use DoctrineORMModule\Collector\MappingCollector; + +/** + * Tests for the MappingCollector + * + * @author Marco Pivetta + * @license MIT + */ +class MappingCollectorTest extends PHPUnit_Framework_TestCase +{ + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $metadataFactory; + + /** + * @var MappingCollector + */ + protected $collector; + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::__construct + */ + public function setUp() + { + parent::setUp(); + + $this->metadataFactory = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadataFactory'); + $this->collector = new MappingCollector($this->metadataFactory, 'test-collector'); + } + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::getName + */ + public function testGetName() + { + $this->assertSame('test-collector', $this->collector->getName()); + } + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::getPriority + */ + public function testGetPriority() + { + $this->assertInternalType('int', $this->collector->getPriority()); + } + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::collect + * @covers \DoctrineORMModule\Collector\MappingCollector::getClasses + */ + public function testCollect() + { + $m1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $m1->expects($this->any())->method('getName')->will($this->returnValue('M1')); + $m2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $m2->expects($this->any())->method('getName')->will($this->returnValue('M2')); + $this + ->metadataFactory + ->expects($this->any()) + ->method('getAllMetadata') + ->will($this->returnValue(array($m1, $m2))); + + $this->collector->collect($this->getMock('Zend\\Mvc\\MvcEvent')); + + $classes = $this->collector->getClasses(); + + $this->assertCount(2, $classes); + $this->assertSame($classes['M1'], $m1); + $this->assertSame($classes['M2'], $m2); + } + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::canHide + */ + public function testCanHide() + { + $this->assertTrue($this->collector->canHide()); + + $m1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $m1->expects($this->any())->method('getName')->will($this->returnValue('M1')); + $this->metadataFactory->expects($this->any())->method('getAllMetadata')->will($this->returnValue(array($m1))); + + $this->collector->collect($this->getMock('Zend\\Mvc\\MvcEvent')); + + $this->assertFalse($this->collector->canHide()); + } + + /** + * @covers \DoctrineORMModule\Collector\MappingCollector::serialize + * @covers \DoctrineORMModule\Collector\MappingCollector::unserialize + * @covers \DoctrineORMModule\Collector\MappingCollector::collect + */ + public function testSerializeUnserializeAndCollectWithNoMetadataFactory() + { + $m1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $m1->expects($this->any())->method('getName')->will($this->returnValue('M1')); + $this->metadataFactory->expects($this->any())->method('getAllMetadata')->will($this->returnValue(array($m1))); + + $this->collector->collect($this->getMock('Zend\\Mvc\\MvcEvent')); + + /* @var $collector MappingCollector */ + $collector = unserialize(serialize($this->collector)); + + $classes = $collector->getClasses(); + $this->assertCount(1, $classes); + $this->assertEquals($m1, $classes['M1']); + $this->assertSame('test-collector', $collector->getName()); + + $collector->collect($this->getMock('Zend\\Mvc\\MvcEvent')); + + $classes = $collector->getClasses(); + $this->assertCount(1, $classes); + $this->assertEquals($m1, $classes['M1']); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/SQLLoggerCollectorTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/SQLLoggerCollectorTest.php new file mode 100644 index 00000000..508627b8 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Collector/SQLLoggerCollectorTest.php @@ -0,0 +1,101 @@ +. + */ + +namespace DoctrineORMModuleTest\Collector; + +use PHPUnit_Framework_TestCase as TestCase; +use DoctrineORMModule\Collector\SQLLoggerCollector; +use Doctrine\DBAL\Logging\DebugStack; +use Zend\Mvc\MvcEvent; + +class SQLLoggerCollectorTest extends TestCase +{ + /** + * @var DebugStack + */ + protected $logger; + + /** + * @var string + */ + protected $name = 'test-collector-name'; + + /** + * @var SQLLoggerCollector + */ + protected $collector; + + public function setUp() + { + parent::setUp(); + $this->logger = new DebugStack(); + $this->collector = new SQLLoggerCollector($this->logger, $this->name); + } + + public function testHasCorrectName() + { + $this->assertSame($this->name, $this->collector->getName()); + } + + public function testGetPriority() + { + $this->assertInternalType('int', $this->collector->getPriority()); + } + + public function testCollect() + { + $this->collector->collect(new MvcEvent()); + } + + public function testCanHide() + { + $this->assertTrue($this->collector->canHide()); + $this->logger->startQuery('some sql'); + $this->assertFalse($this->collector->canHide()); + } + + public function testGetQueryCount() + { + $this->assertSame(0, $this->collector->getQueryCount()); + $this->logger->startQuery('some sql'); + $this->assertSame(1, $this->collector->getQueryCount()); + $this->logger->startQuery('some more sql'); + $this->assertSame(2, $this->collector->getQueryCount()); + } + + public function testGetQueryTime() + { + $start = microtime(true); + $this->assertEquals(0, $this->collector->getQueryTime()); + + $this->logger->startQuery('some sql'); + $this->logger->stopQuery(); + $time = microtime(true) - $start; + $time1 = $this->collector->getQueryTime(); + $this->assertGreaterThan(0, $time1); + $this->assertLessThan($time, $time1); + + $this->logger->startQuery('some more sql'); + $this->logger->stopQuery(); + $time = microtime(true) - $start; + $time2 = $this->collector->getQueryTime(); + $this->assertGreaterThan($time1, $time2); + $this->assertLessThan($time, $time1); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Form/ElementAnnotationsListenerTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Form/ElementAnnotationsListenerTest.php new file mode 100644 index 00000000..a42505d3 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Form/ElementAnnotationsListenerTest.php @@ -0,0 +1,59 @@ +type = $type; + $event->setParam('annotation', $checkboxAnnotation); + $event->setParam('elementSpec', new ArrayObject(array( + 'spec' => array(), + ))); + $listener->handleTypeAnnotation($event); + $spec = $event->getParam('elementSpec'); + $this->assertEquals($expectedType, $spec['spec']['type']); + } + + public function testHandleAnnotationAttributesShallAppent() + { + $listener = new ElementAnnotationsListener(); + $event = new Zend\EventManager\Event(); + $annotation = new Doctrine\ORM\Mapping\Column(); + + $annotation->type = 'text'; + $event->setParam('annotation', $annotation); + $event->setParam('elementSpec', new ArrayObject(array( + 'spec' => array('attributes' => array('attr1' => 'value')), + ))); + + $listener->handleAttributesAnnotation($event); + $spec = $event->getParam('elementSpec'); + $this->assertCount(2, $spec['spec']['attributes']); + $this->assertArrayHasKey('attr1', $spec['spec']['attributes']); + $this->assertEquals('textarea', $spec['spec']['attributes']['type']); + $this->assertEquals('value', $spec['spec']['attributes']['attr1']); + } + + public function eventProvider() + { + return array( + array('bool', 'Zend\Form\Element\Checkbox'), + array('boolean', 'Zend\Form\Element\Checkbox'), + array('string', 'Zend\Form\Element'), + ); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Framework/TestCase.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Framework/TestCase.php new file mode 100644 index 00000000..9a7bd92c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Framework/TestCase.php @@ -0,0 +1,88 @@ +. + */ + +namespace DoctrineORMModuleTest\Framework; + +use PHPUnit_Framework_TestCase; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Tools\SchemaTool; +use Zend\ServiceManager\ServiceManager; +use DoctrineORMModuleTest\Util\ServiceManagerFactory; + +/** + * Base test case for tests using the entity manager + */ +class TestCase extends PHPUnit_Framework_TestCase +{ + /** + * @var boolean + */ + protected $hasDb = false; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * Creates a database if not done already. + */ + public function createDb() + { + if ($this->hasDb) { + return; + } + + $em = $this->getEntityManager(); + $tool = new SchemaTool($em); + $tool->updateSchema($em->getMetadataFactory()->getAllMetadata()); + $this->hasDb = true; + } + + /** + * Drops existing database + */ + public function dropDb() + { + $em = $this->getEntityManager(); + $tool = new SchemaTool($em); + $tool->dropSchema($em->getMetadataFactory()->getAllMetadata()); + $em->clear(); + + $this->hasDb = false; + } + + /** + * Get EntityManager. + * + * @return EntityManager + */ + public function getEntityManager() + { + if ($this->entityManager) { + return $this->entityManager; + } + + $serviceManager = ServiceManagerFactory::getServiceManager(); + $serviceManager->get('doctrine.entity_resolver.orm_default'); + $this->entityManager = $serviceManager->get('doctrine.entitymanager.orm_default'); + + return $this->entityManager; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/ConfigurationOptionsTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/ConfigurationOptionsTest.php new file mode 100644 index 00000000..0bb69000 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/ConfigurationOptionsTest.php @@ -0,0 +1,43 @@ +. + */ + +namespace DoctrineORMModuleTest\Collector; + +use PHPUnit_Framework_TestCase as TestCase; +use DoctrineORMModule\Options\Configuration; + +class ConfigurationOptionsTest extends TestCase +{ + public function testSetGetNamingStrategy() + { + $options = new Configuration(); + $options->setNamingStrategy(null); + $this->assertNull($options->getNamingStrategy()); + + $options->setNamingStrategy('test'); + $this->assertSame('test', $options->getNamingStrategy()); + + $namingStrategy = $this->getMock('Doctrine\ORM\Mapping\NamingStrategy'); + $options->setNamingStrategy($namingStrategy); + $this->assertSame($namingStrategy, $options->getNamingStrategy()); + + $this->setExpectedException('Zend\Stdlib\Exception\InvalidArgumentException'); + $options->setNamingStrategy(new \stdClass()); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/SQLLoggerCollectorOptionsTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/SQLLoggerCollectorOptionsTest.php new file mode 100644 index 00000000..09863790 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Options/SQLLoggerCollectorOptionsTest.php @@ -0,0 +1,54 @@ +. + */ + +namespace DoctrineORMModuleTest\Collector; + +use PHPUnit_Framework_TestCase as TestCase; +use DoctrineORMModule\Options\SQLLoggerCollectorOptions; + +class SQLLoggerCollectorOptionsTest extends TestCase +{ + public function testSetGetSQLLogger() + { + $options = new SQLLoggerCollectorOptions(); + $options->setSqlLogger('sql-logger-name'); + $this->assertSame('sql-logger-name', $options->getSqlLogger()); + $options->setSqlLogger(null); + $this->assertSame(null, $options->getSqlLogger()); + } + + public function testSetGetConfiguration() + { + $options = new SQLLoggerCollectorOptions(); + $options->setConfiguration('configuration-name'); + $this->assertSame('configuration-name', $options->getConfiguration()); + $options->setConfiguration(null); + $this->assertSame('doctrine.configuration.orm_default', $options->getConfiguration()); + } + + public function testSetGetName() + { + $options = new SQLLoggerCollectorOptions(); + $this->assertSame('orm_default', $options->getName()); // testing defaults too! + $options->setName('collector-name'); + $this->assertSame('collector-name', $options->getName()); + $options->setName(null); + $this->assertSame('', $options->getName()); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Paginator/AdapterTestIgnore.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Paginator/AdapterTestIgnore.php new file mode 100644 index 00000000..15e0b089 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Paginator/AdapterTestIgnore.php @@ -0,0 +1,108 @@ +. + */ + +namespace DoctrineORMModuleTest\Paginator; + +use DoctrineORMModuleTest\Framework\TestCase; +use DoctrineORMModuleTest\Assets\Fixture\TestFixture; + +use Doctrine\Common\DataFixtures\Loader as FixtureLoader; +use Doctrine\Common\DataFixtures\Executor\ORMExecutor; +use Doctrine\Common\DataFixtures\Purger\ORMPurger; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator; +use DoctrineORMModule\Paginator\Adapter\DoctrinePaginator as PaginatorAdapter; + +class AdapterTest extends TestCase +{ + /** + * @var QueryBuilder + */ + protected $qb; + + /** + * @var PaginatorAdapter + */ + protected $paginatorAdapter; + + /** + * @var DoctrinePaginator + */ + protected $paginator; + + public function setUp() + { + parent::setUp(); + + $this->createDb(); + $loader = new FixtureLoader(); + $loader->addFixture(new TestFixture()); + $purger = new ORMPurger(); + $executor = new ORMExecutor($this->getEntityManager(), $purger); + $executor->execute($loader->getFixtures()); + + $this->qb = $this + ->getEntityManager() + ->createQueryBuilder() + ->select('t') + ->from('DoctrineORMModuleTest\Assets\Entity\Test', 't') + ->orderBy('t.id', 'ASC'); + + $this->paginator = new DoctrinePaginator($this->qb); + $this->paginatorAdapter = new PaginatorAdapter($this->paginator); + } + + public function testCanSetPaginator() + { + $this->assertSame($this->paginator, $this->paginatorAdapter->getPaginator()); + $doctrinePaginator = new DoctrinePaginator($this->getEntityManager()->createQuery('')); + $this->paginatorAdapter->setPaginator($doctrinePaginator); + $this->assertSame($doctrinePaginator, $this->paginatorAdapter->getPaginator()); + } + + public function testContainedItemsCount() + { + $itemsCount = $this->qb->select('COUNT(t)')->getQuery()->getSingleScalarResult(); + + $this->assertEquals($itemsCount, $this->paginatorAdapter->count()); + $this->assertEquals($itemsCount, count($this->paginatorAdapter->getItems(0, $itemsCount + 100))); + + } + + public function testGetsItemsAtOffsetZero() + { + $expected = $this->qb->setMaxResults(10)->getQuery()->getResult(); + $actual = $this->paginatorAdapter->getItems(0, 10); + + foreach ($expected as $key => $expectedItem) { + $this->assertEquals($expectedItem, $actual[$key]); + } + } + + public function testGetsItemsAtOffsetTen() + { + $expected = $this->qb->setFirstResult(10)->setMaxResults(10)->getQuery()->getResult(); + $actual = $this->paginatorAdapter->getItems(10, 10); + + foreach ($expected as $key => $expectedItem) { + $this->assertEquals($expectedItem, $actual[$key]); + } + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/ConfigurationFactoryTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/ConfigurationFactoryTest.php new file mode 100644 index 00000000..b478acc0 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/ConfigurationFactoryTest.php @@ -0,0 +1,119 @@ +. + */ + +namespace DoctrineORMModuleTest\Service; + +use PHPUnit_Framework_TestCase; +use DoctrineORMModule\Service\ConfigurationFactory; +use Doctrine\Common\Cache\ArrayCache; +use Zend\ServiceManager\ServiceManager; + +class ConfigurationFactoryTest extends PHPUnit_Framework_TestCase +{ + /** + * @var ServiceManager + */ + protected $serviceManager; + + /** + * @var ConfigurationFactory + */ + protected $factory; + + /** + * {@inheritDoc} + */ + public function setUp() + { + $this->serviceManager = new ServiceManager(); + $this->factory = new ConfigurationFactory('test_default'); + $this->serviceManager->setService('doctrine.cache.array', new ArrayCache()); + $this->serviceManager->setService( + 'doctrine.driver.orm_default', + $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver' + )); + } + + public function testWillInstantiateConfigWithoutNamingStrategySetting() + { + $config = array( + 'doctrine' => array( + 'configuration' => array( + 'test_default' => array(), + ), + ), + ); + $this->serviceManager->setService('Config', $config); + $ormConfig = $this->factory->createService($this->serviceManager); + $this->assertInstanceOf('Doctrine\ORM\Mapping\NamingStrategy', $ormConfig->getNamingStrategy()); + } + + public function testWillInstantiateConfigWithNamingStrategyObject() + { + $namingStrategy = $this->getMock('Doctrine\ORM\Mapping\NamingStrategy'); + + $config = array( + 'doctrine' => array( + 'configuration' => array( + 'test_default' => array( + 'naming_strategy' => $namingStrategy, + ), + ), + ), + ); + $this->serviceManager->setService('Config', $config); + $factory = new ConfigurationFactory('test_default'); + $ormConfig = $factory->createService($this->serviceManager); + $this->assertSame($namingStrategy, $ormConfig->getNamingStrategy()); + } + + public function testWillInstantiateConfigWithNamingStrategyReference() + { + $namingStrategy = $this->getMock('Doctrine\ORM\Mapping\NamingStrategy'); + $config = array( + 'doctrine' => array( + 'configuration' => array( + 'test_default' => array( + 'naming_strategy' => 'test_naming_strategy', + ), + ), + ), + ); + $this->serviceManager->setService('Config', $config); + $this->serviceManager->setService('test_naming_strategy', $namingStrategy); + $ormConfig = $this->factory->createService($this->serviceManager); + $this->assertSame($namingStrategy, $ormConfig->getNamingStrategy()); + } + + public function testWillNotInstantiateConfigWithInvalidNamingStrategyReference() + { + $config = array( + 'doctrine' => array( + 'configuration' => array( + 'test_default' => array( + 'naming_strategy' => 'test_naming_strategy', + ), + ), + ), + ); + $this->serviceManager->setService('Config', $config); + $this->setExpectedException('Zend\ServiceManager\Exception\InvalidArgumentException'); + $this->factory->createService($this->serviceManager); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/EntityResolverFactoryTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/EntityResolverFactoryTest.php new file mode 100644 index 00000000..69433012 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/EntityResolverFactoryTest.php @@ -0,0 +1,34 @@ +. + */ + +namespace DoctrineORMModuleTest\Service; + +use DoctrineORMModuleTest\Framework\TestCase as TestCase; + +class EntityResolverFactoryTest extends TestCase +{ + public function testCanResolveTargetEntity() + { + $em = $this->getEntityManager(); + $classMetadata = $em->getClassMetadata('DoctrineORMModuleTest\Assets\Entity\ResolveTarget'); + $meta = $classMetadata->associationMappings; + + $this->assertSame('DoctrineORMModuleTest\Assets\Entity\TargetEntity', $meta['target']['targetEntity']); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/SQLLoggerCollectorFactoryTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/SQLLoggerCollectorFactoryTest.php new file mode 100644 index 00000000..600cbc74 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Service/SQLLoggerCollectorFactoryTest.php @@ -0,0 +1,165 @@ +. + */ + +namespace DoctrineORMModuleTest\Service; + +use PHPUnit_Framework_TestCase as TestCase; +use DoctrineORMModule\Service\SQLLoggerCollectorFactory; +use Doctrine\DBAL\Logging\DebugStack; +use Doctrine\ORM\Configuration as ORMConfiguration; +use Zend\ServiceManager\ServiceManager; + +class SQLLoggerCollectorFactoryTest extends TestCase +{ + /** + * @var ServiceManager + */ + protected $services; + + /** + * @var SQLLoggerCollectorFactory + */ + protected $factory; + + /** + * {@inheritDoc} + */ + public function setUp() + { + parent::setUp(); + $this->services = new ServiceManager(); + $this->factory = new SQLLoggerCollectorFactory('orm_default'); + } + + public function testCreateSQLLoggerCollector() + { + $configuration = new ORMConfiguration(); + $this->services->setService('doctrine.configuration.orm_default', $configuration); + $this->services->setService( + 'Config', + array( + 'doctrine' => array( + 'sql_logger_collector' => array( + 'orm_default' => array(), + ), + ), + ) + ); + $service = $this->factory->createService($this->services); + $this->assertInstanceOf('DoctrineORMModule\Collector\SQLLoggerCollector', $service); + $this->assertInstanceOf('Doctrine\DBAL\Logging\SQLLogger', $configuration->getSQLLogger()); + } + + public function testCreateSQLLoggerWithCustomConfiguration() + { + $configuration = new ORMConfiguration(); + $this->services->setService('configuration_service_id', $configuration); + $this->services->setService( + 'Config', + array( + 'doctrine' => array( + 'sql_logger_collector' => array( + 'orm_default' => array( + 'configuration' => 'configuration_service_id', + ), + ), + ), + ) + ); + $this->factory->createService($this->services); + $this->assertInstanceOf('Doctrine\DBAL\Logging\SQLLogger', $configuration->getSQLLogger()); + } + + public function testCreateSQLLoggerWithPreviousExistingLoggerChainsLoggers() + { + $originalLogger = $this->getMock('Doctrine\DBAL\Logging\SQLLogger'); + $originalLogger + ->expects($this->once()) + ->method('startQuery') + ->with($this->equalTo('test query')); + $injectedLogger = $this->getMock('Doctrine\DBAL\Logging\DebugStack'); + $injectedLogger + ->expects($this->once()) + ->method('startQuery') + ->with($this->equalTo('test query')); + + $configuration = new ORMConfiguration(); + $configuration->setSQLLogger($originalLogger); + $this->services->setService('doctrine.configuration.orm_default', $configuration); + $this->services->setService('custom_logger', $injectedLogger); + $this->services->setService( + 'Config', + array( + 'doctrine' => array( + 'sql_logger_collector' => array( + 'orm_default' => array( + 'sql_logger' => 'custom_logger', + ), + ), + ), + ) + ); + $this->factory->createService($this->services); + /* @var $logger \Doctrine\DBAL\Logging\SQLLogger */ + $logger = $configuration->getSQLLogger(); + $logger->startQuery('test query'); + } + + public function testCreateSQLLoggerWithCustomLogger() + { + $configuration = new ORMConfiguration(); + $logger = new DebugStack(); + $this->services->setService('doctrine.configuration.orm_default', $configuration); + $this->services->setService('logger_service_id', $logger); + $this->services->setService( + 'Config', + array( + 'doctrine' => array( + 'sql_logger_collector' => array( + 'orm_default' => array( + 'sql_logger' => 'logger_service_id', + ), + ), + ), + ) + ); + $this->factory->createService($this->services); + $this->assertSame($logger, $configuration->getSQLLogger()); + } + + public function testCreateSQLLoggerWithCustomName() + { + $this->services->setService('doctrine.configuration.orm_default', new ORMConfiguration()); + $this->services->setService( + 'Config', + array( + 'doctrine' => array( + 'sql_logger_collector' => array( + 'orm_default' => array( + 'name' => 'test_collector_name', + ), + ), + ), + ) + ); + /* @var $service \DoctrineORMModule\Collector\SQLLoggerCollector */ + $service = $this->factory->createService($this->services); + $this->assertSame('test_collector_name', $service->getName()); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Util/ServiceManagerFactory.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Util/ServiceManagerFactory.php new file mode 100644 index 00000000..d4f7f60f --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Util/ServiceManagerFactory.php @@ -0,0 +1,64 @@ +. + */ + +namespace DoctrineORMModuleTest\Util; + +use Zend\ServiceManager\ServiceManager; +use Zend\Mvc\Service\ServiceManagerConfig; + +/** + * Utility used to retrieve a freshly bootstrapped application's service manager + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class ServiceManagerFactory +{ + /** + * @var array + */ + protected static $config; + + /** + * @param array $config + */ + public static function setConfig(array $config) + { + static::$config = $config; + } + + /** + * Builds a new service manager + */ + public static function getServiceManager() + { + $serviceManager = new ServiceManager(new ServiceManagerConfig( + isset(static::$config['service_manager']) ? static::$config['service_manager'] : array() + )); + $serviceManager->setService('ApplicationConfig', static::$config); + $serviceManager->setFactory('ServiceListener', 'Zend\Mvc\Service\ServiceListenerFactory'); + + /** @var $moduleManager \Zend\ModuleManager\ModuleManager */ + $moduleManager = $serviceManager->get('ModuleManager'); + $moduleManager->loadModules(); + //$serviceManager->setAllowOverride(true); + return $serviceManager; + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/MetadataGrapherTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/MetadataGrapherTest.php new file mode 100644 index 00000000..1e46e20c --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/MetadataGrapherTest.php @@ -0,0 +1,395 @@ +. + */ + +namespace DoctrineORMModuleTest\Yuml; + +use DoctrineORMModule\Yuml\MetadataGrapher; +use PHPUnit_Framework_TestCase; + +/** + * Tests for the metadata to string converter + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class MetadataGrapherTest extends PHPUnit_Framework_TestCase +{ + /** + * @var MetadataGrapher + */ + protected $grapher; + + /** + * {@inheritDoc} + */ + public function setUp() + { + parent::setUp(); + + $this->grapher = new MetadataGrapher(); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawSimpleEntity() + { + $class = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class->expects($this->any())->method('getName')->will($this->returnValue('Simple\\Entity')); + $class->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + $class->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + + $this->assertSame('[Simple.Entity]', $this->grapher->generateFromMetadata(array($class))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawSimpleEntityWithFields() + { + $class = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class->expects($this->any())->method('getName')->will($this->returnValue('Simple\\Entity')); + $class->expects($this->any())->method('getFieldNames')->will($this->returnValue(array('a', 'b', 'c'))); + $class->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class->expects($this->any())->method('isIdentifier')->will($this->returnCallback(function ($field) { + return $field === 'a'; + })); + + $this->assertSame('[Simple.Entity|+a;b;c]', $this->grapher->generateFromMetadata(array($class))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawOneToOneUniDirectionalAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]-b 1>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawOneToOneBiDirectionalAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class2->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('b')); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]<>a 1-b 1>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawOneToOneBiDirectionalInverseAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class1->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('a')); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A][B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawOneToManyBiDirectionalAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class2->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('b')); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]<>a 1-b *>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawOneToManyBiDirectionalInverseAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(false)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class2->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('b')); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]<>a *-b 1>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawManyToManyUniDirectionalAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]-b *>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawManyToManyUniDirectionalInverseAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A],[B]-a *>[A]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawManyToManyBiDirectionalAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class2->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('b')); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]<>a *-b *>[B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawManyToManyBiDirectionalInverseAssociation() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(true)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getAssociationMappedByTargetField')->will($this->returnValue('a')); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class2->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A][B]', $this->grapher->generateFromMetadata(array($class1, $class2))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawManyToManyAssociationWithoutKnownInverseSide() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class1->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('b'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('B')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame('[A]<>-b *>[B]', $this->grapher->generateFromMetadata(array($class1))); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawInheritance() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $child = get_class($this->getMock('stdClass')); + $class1->expects($this->any())->method('getName')->will($this->returnValue('stdClass')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + $class2->expects($this->any())->method('getName')->will($this->returnValue($child)); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $this->assertSame( + '[stdClass]^[' . str_replace('\\', '.', $child) . ']', + $this->grapher->generateFromMetadata(array($class2, $class1)) + ); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawInheritedFields() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $child = get_class($this->getMock('stdClass')); + + $class1->expects($this->any())->method('getName')->will($this->returnValue('stdClass')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array('inherited'))); + + $class2->expects($this->any())->method('getName')->will($this->returnValue($child)); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array('inherited', 'field2'))); + + $this->assertSame( + '[stdClass|inherited]^[' . str_replace('\\', '.', $child) . '|field2]', + $this->grapher->generateFromMetadata(array($class2, $class1)) + ); + } + + /** + * @covers \DoctrineORMModule\Yuml\MetadataGrapher + */ + public function testDrawInheritedAssociations() + { + $class1 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class2 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class3 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $class4 = $this->getMock('Doctrine\\Common\\Persistence\\Mapping\\ClassMetadata'); + $child = get_class($this->getMock('stdClass')); + + $class1->expects($this->any())->method('getName')->will($this->returnValue('stdClass')); + $class1->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a'))); + $class1->expects($this->any())->method('getAssociationTargetClass')->will($this->returnValue('A')); + $class1->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class1->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class1->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class2->expects($this->any())->method('getName')->will($this->returnValue($child)); + $class2->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array('a', 'b'))); + $class2 + ->expects($this->any()) + ->method('getAssociationTargetClass') + ->will($this->returnCallback(function ($assoc) { + return strtoupper($assoc); + })); + $class2->expects($this->any())->method('isAssociationInverseSide')->will($this->returnValue(false)); + $class2->expects($this->any())->method('isCollectionValuedAssociation')->will($this->returnValue(true)); + $class2->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class3->expects($this->any())->method('getName')->will($this->returnValue('A')); + $class3->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class3->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $class4->expects($this->any())->method('getName')->will($this->returnValue('B')); + $class4->expects($this->any())->method('getAssociationNames')->will($this->returnValue(array())); + $class4->expects($this->any())->method('getFieldNames')->will($this->returnValue(array())); + + $childName = str_replace('\\', '.', $child); + $this->assertSame( + '[stdClass]<>-a *>[A],[stdClass]^[' . $childName . '],[' . $childName . ']<>-b *>[B]', + $this->grapher->generateFromMetadata(array($class1, $class2)) + ); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/YumlControllerTest.php b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/YumlControllerTest.php new file mode 100644 index 00000000..9b164eb8 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/DoctrineORMModuleTest/Yuml/YumlControllerTest.php @@ -0,0 +1,98 @@ +. + */ + +namespace OcraServiceManagerTest\ServiceManager; + +use DoctrineORMModule\Yuml\YumlController; + +/** + * Tests for Yuml redirector controller + * + * @license MIT + * @link http://www.doctrine-project.org/ + * @author Marco Pivetta + */ +class YumlControlleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var YumlController + */ + protected $controller; + + /** + * @var \Zend\Http\Client|\PHPUnit_Framework_MockObject_MockObject + */ + protected $httpClient; + + /** + * @var \Zend\Mvc\Controller\PluginManager|\PHPUnit_Framework_MockObject_MockObject + */ + protected $pluginManager; + + /** + * {@inheritDoc} + * + * @covers \DoctrineORMModule\Yuml\YumlController::__construct + */ + public function setUp() + { + $this->httpClient = $this->getMock('Zend\\Http\\Client'); + $this->controller = new YumlController($this->httpClient); + $this->pluginManager = $this->getMock('Zend\\Mvc\\Controller\\PluginManager'); + $this->controller->setPluginManager($this->pluginManager); + } + + /** + * @covers \DoctrineORMModule\Yuml\YumlController::indexAction + */ + public function testIndexActionWillRedirectToYuml() + { + $response = $this->getMock('Zend\\Http\\Response'); + $controllerResponse = $this->getMock('Zend\\Http\\Response'); + $redirect = $this->getMock('Zend\\Mvc\\Controller\\Plugin\\Redirect'); + $this->httpClient->expects($this->any())->method('send')->will($this->returnValue($response)); + $response->expects($this->any())->method('isSuccess')->will($this->returnValue(true)); + $response->expects($this->any())->method('getBody')->will($this->returnValue('short-url')); + $this + ->pluginManager + ->expects($this->any()) + ->method('get')->with('redirect') + ->will($this->returnValue($redirect)); + $redirect + ->expects($this->any()) + ->method('toUrl') + ->with('http://yuml.me/short-url') + ->will($this->returnValue($controllerResponse)); + + $this->assertSame($controllerResponse, $this->controller->indexAction()); + } + + /** + * @covers \DoctrineORMModule\Yuml\YumlController::indexAction + */ + public function testIndexActionWillFailOnMalformedResponse() + { + $response = $this->getMock('Zend\\Http\\Response'); + $this->httpClient->expects($this->any())->method('send')->will($this->returnValue($response)); + $response->expects($this->any())->method('isSuccess')->will($this->returnValue(false)); + + $this->setExpectedException('UnexpectedValueException'); + $this->controller->indexAction(); + } +} diff --git a/vendor/doctrine/doctrine-orm-module/tests/TestConfiguration.php.dist b/vendor/doctrine/doctrine-orm-module/tests/TestConfiguration.php.dist new file mode 100644 index 00000000..d40427d2 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/TestConfiguration.php.dist @@ -0,0 +1,30 @@ +. + */ +return array( + 'modules' => array( + 'DoctrineModule', + 'DoctrineORMModule', + ), + 'module_listener_options' => array( + 'config_glob_paths' => array( + __DIR__ . '/testing.config.php', + ), + 'module_paths' => array(), + ), +); \ No newline at end of file diff --git a/vendor/doctrine/doctrine-orm-module/tests/testing.config.php b/vendor/doctrine/doctrine-orm-module/tests/testing.config.php new file mode 100644 index 00000000..35746ed7 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/tests/testing.config.php @@ -0,0 +1,53 @@ +. + */ +return array( + 'doctrine' => array( + 'driver' => array( + 'DoctrineORMModuleTest\Assets\Entity' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array( + __DIR__ . '/DoctrineORMModuleTest/Assets/Entity' + ), + ), + 'orm_default' => array( + 'drivers' => array( + 'DoctrineORMModuleTest\Assets\Entity' => 'DoctrineORMModuleTest\Assets\Entity', + ), + ), + ), + 'entity_resolver' => array( + 'orm_default' => array( + 'resolvers' => array( + 'DoctrineORMModuleTest\Assets\Entity\TargetInterface' => 'DoctrineORMModuleTest\Assets\Entity\TargetEntity', + ), + ), + ), + 'connection' => array( + 'orm_default' => array( + 'configuration' => 'orm_default', + 'eventmanager' => 'orm_default', + 'driverClass' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', + 'params' => array( + 'memory' => true, + ), + ), + ), + ), +); diff --git a/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-mappings.phtml b/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-mappings.phtml new file mode 100644 index 00000000..5bdb87f9 --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-mappings.phtml @@ -0,0 +1,73 @@ + +
+
+ Doctrine ORM (Mappings) + + getClasses()); ?> + mappings + +
+
+ + + DoctrineORMModule + + + + Mappings for + escapeHtml($collector->getName()); ?> + +
+ generateFromMetadata($collector->getClasses()); + ?> + + + + + + + getClasses() as $class): ?> + escapeHtml($class->getName()); ?> +
+ +
+
+
+ diff --git a/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-queries.phtml b/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-queries.phtml new file mode 100644 index 00000000..f0e2dd2e --- /dev/null +++ b/vendor/doctrine/doctrine-orm-module/view/zend-developer-tools/toolbar/doctrine-orm-queries.phtml @@ -0,0 +1,85 @@ + +
+
+ Doctrine ORM (Queries) + + getQueryCount(); ?> + queries in + zendDeveloperToolsTime($collector->getQueryTime()); ?> s + +
+
+ + + DoctrineORMModule + + + + Queries for + getName() ?> + + + getQueries() as $query): ?> +
+ SQL + + + $1', $query['sql']) ?> +
+ + Params + + + $value): ?> +      =>
+ + +
+ + Types + + + $value): ?> +      =>
+ + +
+ + Time + + + + +
+
+ diff --git a/vendor/doctrine/inflector/README.md b/vendor/doctrine/inflector/README.md new file mode 100644 index 00000000..f2d18d0f --- /dev/null +++ b/vendor/doctrine/inflector/README.md @@ -0,0 +1,4 @@ +# Doctrine Inflector + +Doctrine Inflector is a small library that can perform string manipulations +with regard to upper-/lowercase and singular/plural forms of words. diff --git a/vendor/doctrine/inflector/composer.json b/vendor/doctrine/inflector/composer.json new file mode 100644 index 00000000..3d3c3f90 --- /dev/null +++ b/vendor/doctrine/inflector/composer.json @@ -0,0 +1,21 @@ +{ + "name": "doctrine/inflector", + "type": "library", + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "keywords": ["string", "inflection", "singuarlize", "pluarlize"], + "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": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Inflector\\": "lib/" } + } +} diff --git a/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php new file mode 100644 index 00000000..f4e38e76 --- /dev/null +++ b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php @@ -0,0 +1,385 @@ +. + */ + +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. + * + * Plurialize & 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 array + */ + 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)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie' + ), + 'irregular' => array( + 'atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs' + ) + ); + + /** + * Singular inflector rules + * + * @var array + */ + 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', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(cris|ax|test)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', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '' + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss' + ), + 'irregular' => array( + 'foes' => 'foe', + 'waves' => 'wave', + 'curves' => 'curve' + ) + ); + + /** + * Words that should not be inflected + * + * @var array + */ + private static $uninflected = array( + 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', + 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', + 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder', + 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', + 'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media', + 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', + 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', + 'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', + 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes', + 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', '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(); + + /** + * Convert word in to the format for a Doctrine table name. Converts 'ModelName' to 'model_name' + * + * @param string $word Word to tableize + * @return string $word Tableized word + */ + public static function tableize($word) + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName' + * + * @param string $word Word to classify + * @return string $word Classified word + */ + public static function classify($word) + { + return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); + } + + /** + * Camelize a word. This uses the classify() method and turns the first character to lowercase + * + * @param string $word + * @return string $word + */ + public static function camelize($word) + { + return lcfirst(self::classify($word)); + } + + /** + * Clears Inflectors inflected value caches. And resets the inflection + * rules to the initial values. + * + * @return void + */ + public static function reset() + { + 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 $rules 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($type, $rules, $reset = false) + { + foreach ($rules as $rule => $pattern) { + if (is_array($pattern)) { + if ($reset) { + self::${$type}[$rule] = $pattern; + } else { + if ($rule === 'uninflected') { + self::${$type}[$rule] = array_merge($pattern, self::${$type}[$rule]); + } else { + 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']; + } + + /** + * Return $word in plural form. + * + * @param string $word Word in singular + * @return string Word in plural + */ + public static function pluralize($word) + { + 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] . substr($word, 0, 1) . 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]; + } + } + } + + /** + * Return $word in singular form. + * + * @param string $word Word in plural + * @return string Word in singular + */ + public static function singularize($word) + { + 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'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')'; + self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . 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/inflector/phpunit.xml.dist b/vendor/doctrine/inflector/phpunit.xml.dist new file mode 100644 index 00000000..ef07faa5 --- /dev/null +++ b/vendor/doctrine/inflector/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php new file mode 100644 index 00000000..487c2cd4 --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php @@ -0,0 +1,185 @@ +assertEquals($singular, Inflector::singularize($plural), "'$plural' should be singularized to '$singular'"); + } + + /** + * testInflectingPlurals method + * + * @dataProvider dataSampleWords + * @return void + */ + public function testInflectingPlurals($singular, $plural) { + $this->assertEquals($plural, Inflector::pluralize($singular), "'$singular' should be pluralized to '$plural'"); + } + + /** + * testCustomPluralRule method + * + * @return void + */ + public function testCustomPluralRule() { + Inflector::reset(); + Inflector::rules('plural', array('/^(custom)$/i' => '\1izables')); + $this->assertEquals(Inflector::pluralize('custom'), 'customizables'); + + Inflector::rules('plural', array('uninflected' => array('uninflectable'))); + $this->assertEquals(Inflector::pluralize('uninflectable'), 'uninflectable'); + + Inflector::rules('plural', array( + 'rules' => array('/^(alert)$/i' => '\1ables'), + 'uninflected' => array('noflect', 'abtuse'), + 'irregular' => array('amaze' => 'amazable', 'phone' => 'phonezes') + )); + $this->assertEquals(Inflector::pluralize('noflect'), 'noflect'); + $this->assertEquals(Inflector::pluralize('abtuse'), 'abtuse'); + $this->assertEquals(Inflector::pluralize('alert'), 'alertables'); + $this->assertEquals(Inflector::pluralize('amaze'), 'amazable'); + $this->assertEquals(Inflector::pluralize('phone'), 'phonezes'); + } + + /** + * testCustomSingularRule method + * + * @return void + */ + public function testCustomSingularRule() { + Inflector::reset(); + Inflector::rules('singular', array('/(eple)r$/i' => '\1', '/(jente)r$/i' => '\1')); + + $this->assertEquals(Inflector::singularize('epler'), 'eple'); + $this->assertEquals(Inflector::singularize('jenter'), 'jente'); + + Inflector::rules('singular', array( + 'rules' => array('/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta'), + 'uninflected' => array('singulars'), + 'irregular' => array('spins' => 'spinor') + )); + + $this->assertEquals(Inflector::singularize('inflectors'), 'inflecta'); + $this->assertEquals(Inflector::singularize('contributors'), 'contributa'); + $this->assertEquals(Inflector::singularize('spins'), 'spinor'); + $this->assertEquals(Inflector::singularize('singulars'), 'singulars'); + } + + /** + * test that setting new rules clears the inflector caches. + * + * @return void + */ + public function testRulesClearsCaches() { + Inflector::reset(); + $this->assertEquals(Inflector::singularize('Bananas'), 'Banana'); + $this->assertEquals(Inflector::pluralize('Banana'), 'Bananas'); + + Inflector::rules('singular', array( + 'rules' => array('/(.*)nas$/i' => '\1zzz') + )); + $this->assertEquals('Banazzz', Inflector::singularize('Bananas'), 'Was inflected with old rules.'); + + Inflector::rules('plural', array( + 'rules' => array('/(.*)na$/i' => '\1zzz'), + 'irregular' => array('corpus' => 'corpora') + )); + $this->assertEquals(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules.'); + $this->assertEquals(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form.'); + } + + /** + * Test resetting inflection rules. + * + * @return void + */ + public function testCustomRuleWithReset() { + Inflector::reset(); + $uninflected = array('atlas', 'lapis', 'onibus', 'pires', 'virus', '.*x'); + $pluralIrregular = array('as' => 'ases'); + + Inflector::rules('singular', array( + 'rules' => array('/^(.*)(a|e|o|u)is$/i' => '\1\2l'), + 'uninflected' => $uninflected, + ), true); + + Inflector::rules('plural', array( + 'rules' => array( + '/^(.*)(a|e|o|u)l$/i' => '\1\2is', + ), + 'uninflected' => $uninflected, + 'irregular' => $pluralIrregular + ), true); + + $this->assertEquals(Inflector::pluralize('Alcool'), 'Alcoois'); + $this->assertEquals(Inflector::pluralize('Atlas'), 'Atlas'); + $this->assertEquals(Inflector::singularize('Alcoois'), 'Alcool'); + $this->assertEquals(Inflector::singularize('Atlas'), 'Atlas'); + } +} + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 00000000..e8323d29 --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } + } +} 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 00000000..eb6cf7c0 --- /dev/null +++ b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php @@ -0,0 +1,265 @@ +. + */ + +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 +{ + /** + * @var array Array of scanned tokens + */ + private $tokens = array(); + + /** + * @var integer Current lexer position in input string + */ + private $position = 0; + + /** + * @var integer Current peek of current lexer position + */ + private $peek = 0; + + /** + * @var array The next token in the input. + */ + public $lookahead; + + /** + * @var array The last matched/seen token. + */ + 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. + */ + public function setInput($input) + { + $this->tokens = array(); + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + */ + 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 + */ + public function resetPosition($position = 0) + { + $this->position = $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. + * + * A 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 + * + * @return array|null the next token; null if there is no more tokens left + */ + 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. + */ + 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 + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = '/(' . implode(')|(', $this->getCatchablePatterns()) . ')|' + . implode('|', $this->getNonCatchablePatterns()) . '/i'; + } + + $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; + } + + /** + * 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/mongodb-odm/.gitignore b/vendor/doctrine/mongodb-odm/.gitignore new file mode 100644 index 00000000..0c3dfbc1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/.gitignore @@ -0,0 +1,14 @@ +tools/sandbox/Proxies/* +tools/sandbox/Hydrators/* +tests/Proxies/* +tests/Hydrators/* +build.properties +build/ +logs/ +reports/ +dist/ +phpunit.xml +vendor +composer.lock +composer.phar +.idea/ diff --git a/vendor/doctrine/mongodb-odm/.travis.yml b/vendor/doctrine/mongodb-odm/.travis.yml new file mode 100644 index 00000000..dc33a2ec --- /dev/null +++ b/vendor/doctrine/mongodb-odm/.travis.yml @@ -0,0 +1,15 @@ +language: php + +php: + - 5.3 + - 5.4 + +env: + - MONGO_VERSION=1.2.12 + - MONGO_VERSION=1.3.4 + +services: mongodb + +before_script: + - pecl -q install mongo-${MONGO_VERSION} && echo "extension=mongo.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` + - composer install --dev diff --git a/vendor/doctrine/mongodb-odm/LICENSE b/vendor/doctrine/mongodb-odm/LICENSE new file mode 100644 index 00000000..4a91f0bf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 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/mongodb-odm/README.markdown b/vendor/doctrine/mongodb-odm/README.markdown new file mode 100644 index 00000000..b28b4332 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/README.markdown @@ -0,0 +1,13 @@ +# Doctrine MongoDB Object Document Mapper + +[![Build Status](https://secure.travis-ci.org/doctrine/mongodb-odm.png?branch=master)](http://travis-ci.org/doctrine/mongodb-odm) + +The Doctrine MongoDB ODM project is a library that provides a PHP object mapping functionality for MongoDB. + +## More resources: + +* [Website](http://docs.doctrine-project.org/projects/doctrine-mongodb-odm) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/index.html) +* [API](http://www.doctrine-project.org/api/mongodb_odm/1.0/index.html) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/MODM) +* [Downloads](http://github.com/doctrine/mongodb-odm/downloads) diff --git a/vendor/doctrine/mongodb-odm/build.properties.dev b/vendor/doctrine/mongodb-odm/build.properties.dev new file mode 100644 index 00000000..4d9effd7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/build.properties.dev @@ -0,0 +1,11 @@ +version=1.0.0BETA4 +stability=beta +build.dir=build +dist.dir=dist +report.dir=reports +log.archive.dir=logs +test.phpunit_configuration_file= +test.phpunit_generate_coverage=0 +test.pmd_reports=0 +test.pdepend_exec= +test.phpmd_exec= \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/build.xml b/vendor/doctrine/mongodb-odm/build.xml new file mode 100644 index 00000000..2354963a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/build.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DoctrineMongoDBODM + Doctrine MongoDB Object Document Mapper + pear.doctrine-project.org + The Doctrine MongoDB ODM package is the package that offers transparent persistence to MongoDB. + + + LGPL + + + - + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/composer.json b/vendor/doctrine/mongodb-odm/composer.json new file mode 100644 index 00000000..b3b546e2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/composer.json @@ -0,0 +1,34 @@ +{ + "name": "doctrine/mongodb-odm", + "type": "library", + "description": "Doctrine MongoDB Object Document Mapper", + "keywords": ["odm", "database", "persistence", "mongodb"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + { "name": "Bulat Shakirzyanov", "email": "mallluhuct@gmail.com" }, + { "name": "Kris Wallsmith", "email": "kris.wallsmith@gmail.com" }, + { "name": "Jonathan H. Wage", "email": "jonwage@gmail.com" }, + { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" } + ], + "require": { + "php": ">=5.3.2", + "symfony/console": ">=2.0-dev,<2.3-dev", + "doctrine/common": ">=2.2.0,<2.5-dev", + "doctrine/mongodb": "1.0.*" + }, + "require-dev": { + "symfony/yaml": ">=2.0-dev,<2.3-dev" + }, + "suggest": { + "symfony/yaml": "Enables the YAML metadata mapping driver" + }, + "autoload": { + "psr-0": { "Doctrine\\ODM\\MongoDB": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/mongodb-odm/doctrine-mongo-mapping.xsd b/vendor/doctrine/mongodb-odm/doctrine-mongo-mapping.xsd new file mode 100644 index 00000000..a5262d2b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/doctrine-mongo-mapping.xsd @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Configuration.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Configuration.php new file mode 100644 index 00000000..a50e931d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -0,0 +1,369 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\ODM\MongoDB\Mapping\Driver\PHPDriver; +use Doctrine\Common\Cache\Cache; + +/** + * Configuration class for the DocumentManager. When setting up your DocumentManager + * you can optionally specify an instance of this class as the second argument. + * If you do not pass a configuration object, a blank one will be created for you. + * + * + * @author Roman Borschel + */ +class Configuration extends \Doctrine\MongoDB\Configuration +{ + /** + * Adds a namespace under a certain alias. + * + * @param string $alias + * @param string $namespace + */ + public function addDocumentNamespace($alias, $namespace) + { + $this->attributes['documentNamespaces'][$alias] = $namespace; + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * @param string $documentNamespaceAlias + * @return string + * @throws MongoDBException + */ + public function getDocumentNamespace($documentNamespaceAlias) + { + if ( ! isset($this->attributes['documentNamespaces'][$documentNamespaceAlias])) { + throw MongoDBException::unknownDocumentNamespace($documentNamespaceAlias); + } + + return trim($this->attributes['documentNamespaces'][$documentNamespaceAlias], '\\'); + } + + /** + * Retrieves the list of registered document namespace aliases. + * + * @return array + */ + public function getDocumentNamespaces() + { + return $this->attributes['documentNamespaces']; + } + + /** + * Set the document alias map + * + * @param array $documentAliasMap + * @return void + */ + public function setDocumentNamespaces(array $documentNamespaces) + { + $this->attributes['documentNamespaces'] = $documentNamespaces; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param MappingDriver $driverImpl + * @todo Force parameter to be a Closure to ensure lazy evaluation + * (as soon as a metadata cache is in effect, the driver never needs to initialize). + */ + public function setMetadataDriverImpl(MappingDriver $driverImpl) + { + $this->attributes['metadataDriverImpl'] = $driverImpl; + } + + /** + * Add a new default annotation driver with a correctly configured annotation reader. + * + * @param array $paths + * @return Mapping\Driver\AnnotationDriver + */ + public function newDefaultAnnotationDriver($paths = array()) + { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + + return new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader, (array) $paths); + } + + /** + * Gets the cache driver implementation that is used for the mapping metadata. + * + * @return MappingDriver + */ + public function getMetadataDriverImpl() + { + return isset($this->attributes['metadataDriverImpl']) ? + $this->attributes['metadataDriverImpl'] : null; + } + + /** + * Gets the cache driver implementation that is used for metadata caching. + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getMetadataCacheImpl() + { + return isset($this->attributes['metadataCacheImpl']) ? + $this->attributes['metadataCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setMetadataCacheImpl(Cache $cacheImpl) + { + $this->attributes['metadataCacheImpl'] = $cacheImpl; + } + + /** + * Sets the directory where Doctrine generates any necessary proxy class files. + * + * @param string $dir + */ + public function setProxyDir($dir) + { + $this->attributes['proxyDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates any necessary proxy class files. + * + * @return string + */ + public function getProxyDir() + { + return isset($this->attributes['proxyDir']) ? + $this->attributes['proxyDir'] : null; + } + + /** + * Gets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @return boolean + */ + public function getAutoGenerateProxyClasses() + { + return isset($this->attributes['autoGenerateProxyClasses']) ? + $this->attributes['autoGenerateProxyClasses'] : true; + } + + /** + * Sets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @param boolean $bool + */ + public function setAutoGenerateProxyClasses($bool) + { + $this->attributes['autoGenerateProxyClasses'] = $bool; + } + + /** + * Gets the namespace where proxy classes reside. + * + * @return string + */ + public function getProxyNamespace() + { + return isset($this->attributes['proxyNamespace']) ? + $this->attributes['proxyNamespace'] : null; + } + + /** + * Sets the namespace where proxy classes reside. + * + * @param string $ns + */ + public function setProxyNamespace($ns) + { + $this->attributes['proxyNamespace'] = $ns; + } + + /** + * Sets the directory where Doctrine generates hydrator class files. + * + * @param string $dir + */ + public function setHydratorDir($dir) + { + $this->attributes['hydratorDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates hydrator class files. + * + * @return string + */ + public function getHydratorDir() + { + return isset($this->attributes['hydratorDir']) ? + $this->attributes['hydratorDir'] : null; + } + + /** + * Gets a boolean flag that indicates whether hydrator classes should always be regenerated + * during each script execution. + * + * @return boolean + */ + public function getAutoGenerateHydratorClasses() + { + return isset($this->attributes['autoGenerateHydratorClasses']) ? + $this->attributes['autoGenerateHydratorClasses'] : true; + } + + /** + * Sets a boolean flag that indicates whether hydrator classes should always be regenerated + * during each script execution. + * + * @param boolean $bool + */ + public function setAutoGenerateHydratorClasses($bool) + { + $this->attributes['autoGenerateHydratorClasses'] = $bool; + } + + /** + * Gets the namespace where hydrator classes reside. + * + * @return string + */ + public function getHydratorNamespace() + { + return isset($this->attributes['hydratorNamespace']) ? + $this->attributes['hydratorNamespace'] : null; + } + + /** + * Sets the namespace where hydrator classes reside. + * + * @param string $ns + */ + public function setHydratorNamespace($ns) + { + $this->attributes['hydratorNamespace'] = $ns; + } + + + /** + * Sets the default DB to use for all Documents that do not specify + * a database. + * + * @param string $defaultDB + */ + public function setDefaultDB($defaultDB) + { + $this->attributes['defaultDB'] = $defaultDB; + } + + /** + * Gets the default DB to use for all Documents that do not specify a database. + * + * @return string $defaultDB + */ + public function getDefaultDB() + { + return isset($this->attributes['defaultDB']) ? + $this->attributes['defaultDB'] : null; + } + + /** + * Set the class metadata factory class name. + * + * @param string $cmf + */ + public function setClassMetadataFactoryName($cmfName) + { + $this->_attributes['classMetadataFactoryName'] = $cmfName; + } + + /** + * Gets the class metadata factory class name. + * + * @return string + */ + public function getClassMetadataFactoryName() + { + if ( ! isset($this->_attributes['classMetadataFactoryName'])) { + $this->_attributes['classMetadataFactoryName'] = 'Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory'; + } + return $this->_attributes['classMetadataFactoryName']; + } + + /** + * Gets array of default commit options. + * + * @return boolean + */ + public function getDefaultCommitOptions() + { + return isset($this->attributes['defaultCommitOptions']) ? + $this->attributes['defaultCommitOptions'] : array('safe' => true); + } + + /** + * Sets array of default commit options. + * + * @param boolean $defaultCommitOptions + */ + public function setDefaultCommitOptions($defaultCommitOptions) + { + $this->attributes['defaultCommitOptions'] = $defaultCommitOptions; + } + + /** + * Add a filter to the list of possible filters. + * + * @param string $name The name of the filter. + * @param string $className The class name of the filter. + */ + public function addFilter($name, $className) + { + $this->attributes['filters'][$name] = $className; + } + + /** + * Gets the class name for a given filter name. + * + * @param string $name The name of the filter. + * + * @return string The class name of the filter, or null of it is not + * defined. + */ + public function getFilterClassName($name) + { + return isset($this->attributes['filters'][$name]) + ? $this->attributes['filters'][$name] + : null; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Cursor.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Cursor.php new file mode 100644 index 00000000..f0695aab --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Cursor.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\MongoDB\Collection; +use Doctrine\MongoDB\Connection; +use Doctrine\MongoDB\Cursor as BaseCursor; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Query\Query; + +/** + * Cursor extends the default Doctrine\MongoDB\Cursor implementation and changes the default + * data returned to be mapped Doctrine document class instances. To disable the hydration + * use hydrate(false) and the Cursor will give you normal document arrays instance of objects. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class Cursor extends BaseCursor +{ + /** + * The Doctrine\MongoDB\Cursor this object is wrapping + * + * @var Doctrine\MongoDB\Cursor $baseCursor + */ + private $baseCursor; + + /** + * Whether or not to hydrate the data to documents. + * + * @var boolean + */ + private $hydrate = true; + + /** + * Whether or not to refresh the data for documents that are already in the identity map. + * + * @var boolean + */ + private $refresh = false; + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $unitOfWork; + + /** + * The ClassMetadata instance. + * + * @var Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + private $class; + + /** @override */ + public function __construct(Connection $connection, Collection $collection, UnitOfWork $uow, ClassMetadata $class, BaseCursor $baseCursor, array $query = array(), array $fields = array(), $numRetries = 0) + { + parent::__construct($connection, $collection, $baseCursor->getMongoCursor(), $query, $fields, $numRetries); + $this->baseCursor = $baseCursor; + $this->unitOfWork = $uow; + $this->class = $class; + } + + /** + * Gets the base cursor. + * + * @return Doctrine\MongoDB\Cursor $baseCursor + */ + public function getBaseCursor() + { + return $this->baseCursor; + } + + /** + * Set hints to account for during reconstitution/lookup of the documents. + * + * @param array $hints + */ + public function setHints(array $hints) + { + $this->hints = $hints; + } + + /** + * Get hints to account for during reconstitution/lookup of the documents. + * + * @return array $hints + */ + public function getHints() + { + return $this->hints; + } + + /** @override */ + public function current() + { + $current = parent::current(); + if ($current && $this->hydrate) { + return $this->unitOfWork->getOrCreateDocument($this->class->name, $current, $this->hints); + } + return $current ? $current : null; + } + + /** @override */ + public function getNext() + { + $next = parent::getNext(); + if ($next && $this->hydrate) { + return $this->unitOfWork->getOrCreateDocument($this->class->name, $next, $this->hints); + } + return $next ? $next : null; + } + + /** + * Set whether to hydrate the documents to objects or not. + * + * @param boolean $bool + */ + public function hydrate($bool = true) + { + $this->hydrate = $bool; + return $this; + } + + /** + * Sets whether to refresh the documents data if it already exists in the identity map. + * + * @param boolean $bool + */ + public function refresh($bool = true) + { + $this->refresh = $bool; + if ($this->refresh) { + $this->hints[Query::HINT_REFRESH] = true; + } else { + unset($this->hints[Query::HINT_REFRESH]); + } + return $this; + } + + /** @override */ + public function slaveOkay($okay = true) + { + if ($okay) { + $this->hints[Query::HINT_SLAVE_OKAY] = true; + } else { + unset($this->hints[Query::HINT_SLAVE_OKAY]); + } + parent::slaveOkay($okay); + return $this; + } + + /** @override */ + public function sort($fields) + { + $fields = $this->unitOfWork + ->getDocumentPersister($this->class->name) + ->prepareSort($fields); + $fields = parent::sort($fields); + return $this; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentManager.php new file mode 100644 index 00000000..913c7247 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -0,0 +1,746 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; +use Doctrine\ODM\MongoDB\Mapping\Driver\PHPDriver; +use Doctrine\MongoDB\Connection; +use Doctrine\ODM\MongoDB\PersistentCollection; +use Doctrine\ODM\MongoDB\Proxy\ProxyFactory; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\EventManager; +use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ODM\MongoDB\Query\FilterCollection; + +/** + * The DocumentManager class is the central access point for managing the + * persistence of documents. + * + * + * @author Roman Borschel + */ +class DocumentManager implements ObjectManager +{ + /** + * The Doctrine MongoDB connection instance. + * + * @var \Doctrine\MongoDB\Connection + */ + private $connection; + + /** + * The used Configuration. + * + * @var \Doctrine\ODM\MongoDB\Configuration + */ + private $config; + + /** + * The metadata factory, used to retrieve the ODM metadata of document classes. + * + * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * The DocumentRepository instances. + * + * @var array + */ + private $repositories = array(); + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var UnitOfWork + */ + private $unitOfWork; + + /** + * The event manager that is the central point of the event system. + * + * @var Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * The Hydrator factory instance. + * + * @var HydratorFactory + */ + private $hydratorFactory; + + /** + * SchemaManager instance + * + * @var Doctrine\ODM\MongoDB\SchemaManager + */ + private $schemaManager; + + /** + * Array of cached document database instances that are lazily loaded. + * + * @var array + */ + private $documentDatabases = array(); + + /** + * Array of cached document collection instances that are lazily loaded. + * + * @var array + */ + private $documentCollections = array(); + + /** + * Whether the DocumentManager is closed or not. + * + * @var bool + */ + private $closed = false; + + /** + * Mongo command character + * + * @var string + */ + private $cmd; + + + /** + * Collection of query filters. + * + * @var \Doctrine\ODM\MongoDB\Query\FilterCollection + */ + private $filterCollection; + + /** + * Creates a new Document that operates on the given Mongo connection + * and uses the given Configuration. + * + * @param \Doctrine\MongoDB\Connection|null $conn + * @param Configuration|null $config + * @param \Doctrine\Common\EventManager|null $eventManager + */ + protected function __construct(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null) + { + $this->config = $config ?: new Configuration(); + $this->eventManager = $eventManager ?: new EventManager(); + $this->cmd = $this->config->getMongoCmd(); + $this->connection = $conn ?: new Connection(null, array(), $this->config, $this->eventManager); + + $metadataFactoryClassName = $this->config->getClassMetadataFactoryName(); + $this->metadataFactory = new $metadataFactoryClassName(); + $this->metadataFactory->setDocumentManager($this); + $this->metadataFactory->setConfiguration($this->config); + if ($cacheDriver = $this->config->getMetadataCacheImpl()) { + $this->metadataFactory->setCacheDriver($cacheDriver); + } + + $hydratorDir = $this->config->getHydratorDir(); + $hydratorNs = $this->config->getHydratorNamespace(); + $this->hydratorFactory = new HydratorFactory( + $this, + $this->eventManager, + $hydratorDir, + $hydratorNs, + $this->config->getAutoGenerateHydratorClasses(), + $this->config->getMongoCmd() + ); + + $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory, $this->cmd); + $this->hydratorFactory->setUnitOfWork($this->unitOfWork); + $this->schemaManager = new SchemaManager($this, $this->metadataFactory); + $this->proxyFactory = new ProxyFactory($this, + $this->config->getProxyDir(), + $this->config->getProxyNamespace(), + $this->config->getAutoGenerateProxyClasses() + ); + } + + /** + * Gets the proxy factory used by the DocumentManager to create document proxies. + * + * @return ProxyFactory + */ + public function getProxyFactory() + { + return $this->proxyFactory; + } + + /** + * Creates a new Document that operates on the given Mongo connection + * and uses the given Configuration. + * + * @static + * @param \Doctrine\MongoDB\Connection|null $conn + * @param Configuration|null $config + * @param \Doctrine\Common\EventManager|null $eventManager + * @return DocumentManager + */ + public static function create(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null) + { + return new DocumentManager($conn, $config, $eventManager); + } + + /** + * Gets the EventManager used by the DocumentManager. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * Gets the PHP Mongo instance that this DocumentManager wraps. + * + * @return \Doctrine\MongoDB\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory() + { + return $this->metadataFactory; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects. + * + * @param object $obj + */ + public function initializeObject($obj) + { + $this->unitOfWork->initializeObject($obj); + } + + /** + * Gets the UnitOfWork used by the DocumentManager to coordinate operations. + * + * @return UnitOfWork + */ + public function getUnitOfWork() + { + return $this->unitOfWork; + } + + /** + * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators + * for each type of document. + * + * @return \Doctrine\ODM\MongoDB\Hydrator\HydratorInterface + */ + public function getHydratorFactory() + { + return $this->hydratorFactory; + } + + /** + * Returns SchemaManager, used to create/drop indexes/collections/databases. + * + * @return \Doctrine\ODM\MongoDB\SchemaManager + */ + public function getSchemaManager() + { + return $this->schemaManager; + } + + /** + * Returns the metadata for a class. + * + * @param string $className The class name. + * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadata + * @internal Performance-sensitive method. + */ + public function getClassMetadata($className) + { + return $this->metadataFactory->getMetadataFor($className); + } + + /** + * Returns the MongoDB instance for a class. + * + * @param string $className The class name. + * @return \Doctrine\MongoDB\Database + */ + public function getDocumentDatabase($className) + { + if (isset($this->documentDatabases[$className])) { + return $this->documentDatabases[$className]; + } + $metadata = $this->metadataFactory->getMetadataFor($className); + $db = $metadata->getDatabase(); + $db = $db ? $db : $this->config->getDefaultDB(); + $db = $db ? $db : 'doctrine'; + $this->documentDatabases[$className] = $this->connection->selectDatabase($db); + return $this->documentDatabases[$className]; + } + + /** + * Gets the array of instantiated document database instances. + * + * @return array + */ + public function getDocumentDatabases() + { + return $this->documentDatabases; + } + + /** + * Returns the MongoCollection instance for a class. + * + * @param string $className The class name. + * @return \Doctrine\MongoDB\Collection + */ + public function getDocumentCollection($className) + { + $metadata = $this->metadataFactory->getMetadataFor($className); + $collection = $metadata->getCollection(); + + if ( ! $collection) { + throw MongoDBException::documentNotMappedToCollection($className); + } + + $db = $this->getDocumentDatabase($className); + if ( ! isset($this->documentCollections[$className])) { + if ($metadata->isFile()) { + $this->documentCollections[$className] = $db->getGridFS($collection); + } else { + $this->documentCollections[$className] = $db->selectCollection($collection); + } + } + $collection = $this->documentCollections[$className]; + if (isset($metadata->slaveOkay)) { + $collection->setSlaveOkay($metadata->slaveOkay); + } + return $this->documentCollections[$className]; + } + + /** + * Gets the array of instantiated document collection instances. + * + * @return array + */ + public function getDocumentCollections() + { + return $this->documentCollections; + } + + /** + * Create a new Query instance for a class. + * + * @param string $documentName The document class name. + * @return Query\Builder + */ + public function createQueryBuilder($documentName = null) + { + return new Query\Builder($this, $this->cmd, $documentName); + } + + /** + * Tells the DocumentManager to make an instance managed and persistent. + * + * The document will be entered into the database at or before transaction + * commit or as a result of the flush operation. + * + * NOTE: The persist operation always considers documents that are not yet known to + * this DocumentManager as NEW. Do not pass detached documents to the persist operation. + * + * @param object $document The instance to make managed and persistent. + */ + public function persist($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->errorIfClosed(); + $this->unitOfWork->persist($document); + } + + /** + * Removes a document instance. + * + * A removed document will be removed from the database at or before transaction commit + * or as a result of the flush operation. + * + * @param object $document The document instance to remove. + */ + public function remove($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->errorIfClosed(); + $this->unitOfWork->remove($document); + } + + /** + * Refreshes the persistent state of a document from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $document The document to refresh. + */ + public function refresh($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->errorIfClosed(); + $this->unitOfWork->refresh($document); + } + + /** + * Detaches a document from the DocumentManager, causing a managed document to + * become detached. Unflushed changes made to the document if any + * (including removal of the document), will not be synchronized to the database. + * Documents which previously referenced the detached document will continue to + * reference it. + * + * @param object $document The document to detach. + */ + public function detach($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->unitOfWork->detach($document); + } + + /** + * Merges the state of a detached document into the persistence context + * of this DocumentManager and returns the managed copy of the document. + * The document passed to merge will not become associated/managed with this DocumentManager. + * + * @param object $document The detached document to merge into the persistence context. + * @return object The managed copy of the document. + */ + public function merge($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->errorIfClosed(); + return $this->unitOfWork->merge($document); + } + + /** + * Acquire a lock on the given document. + * + * @param object $document + * @param int $lockMode + * @param int $lockVersion + * @throws LockException + * @throws LockException + */ + public function lock($document, $lockMode, $lockVersion = null) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->unitOfWork->lock($document, $lockMode, $lockVersion); + } + + /** + * Releases a lock on the given document. + * + * @param object $document + */ + public function unlock($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->unitOfWork->unlock($document); + } + + /** + * Gets the repository for a document class. + * + * @param string $documentName The name of the Document. + * @return DocumentRepository The repository. + */ + public function getRepository($documentName) + { + if (isset($this->repositories[$documentName])) { + return $this->repositories[$documentName]; + } + + $metadata = $this->getClassMetadata($documentName); + $customRepositoryClassName = $metadata->customRepositoryClassName; + + if ($customRepositoryClassName !== null) { + $repository = new $customRepositoryClassName($this, $this->unitOfWork, $metadata); + } else { + $repository = new DocumentRepository($this, $this->unitOfWork, $metadata); + } + + $this->repositories[$documentName] = $repository; + + return $repository; + } + + /** + * 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. + * + * @param object $document + * @param array $options Array of options to be used with batchInsert(), update() and remove() + */ + public function flush($document = null, array $options = array()) + { + if (null !== $document && !is_object($document) && !is_array($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + $this->errorIfClosed(); + $this->unitOfWork->commit($document, $options); + } + + /** + * Gets a reference to the document identified by the given type and identifier + * without actually loading it. + * + * If partial objects are allowed, this method will return a partial object that only + * has its identifier populated. Otherwise a proxy is returned that automatically + * loads itself on first access. + * + * @param string $documentName + * @param string|object $identifier + * @return mixed|object The document reference. + */ + public function getReference($documentName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor($documentName); + + // Check identity map first, if its already in there just return it. + if ($document = $this->unitOfWork->tryGetById($identifier, $class->rootDocumentName)) { + return $document; + } + + $document = $this->proxyFactory->getProxy($class->name, $identifier); + $this->unitOfWork->registerManaged($document, $identifier, array()); + + return $document; + } + + /** + * Gets a partial reference to the document identified by the given type and identifier + * without actually loading it, if the document is not yet loaded. + * + * The returned reference may be a partial object if the document is not yet loaded/managed. + * If it is a partial object it will not initialize the rest of the document state on access. + * Thus you can only ever safely access the identifier of an document obtained through + * this method. + * + * The use-cases for partial references involve maintaining bidirectional associations + * without loading one side of the association or to update an document without loading it. + * Note, however, that in the latter case the original (persistent) document data will + * never be visible to the application (especially not event listeners) as it will + * never be loaded in the first place. + * + * @param string $documentName The name of the document type. + * @param mixed $identifier The document identifier. + * @return object The (partial) document reference. + */ + public function getPartialReference($documentName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor($documentName); + + // Check identity map first, if its already in there just return it. + if ($document = $this->unitOfWork->tryGetById($identifier, $class->rootDocumentName)) { + return $document; + } + $document = $class->newInstance(); + $class->setIdentifierValue($document, $identifier); + $this->unitOfWork->registerManaged($document, $identifier, array()); + + return $document; + } + + /** + * Finds a Document by its identifier. + * + * This is just a convenient shortcut for getRepository($documentName)->find($id). + * + * @param string $documentName + * @param mixed $identifier + * @param int $lockMode + * @param int $lockVersion + * @return object $document + */ + public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null) + { + return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion); + } + + /** + * Clears the DocumentManager. + * + * All documents that are currently managed by this DocumentManager become + * detached. + * + * @param string|null $documentName if given, only documents of this type will get detached + */ + public function clear($documentName = null) + { + $this->unitOfWork->clear($documentName); + } + + /** + * Closes the DocumentManager. All documents that are currently managed + * by this DocumentManager become detached. The DocumentManager may no longer + * be used after it is closed. + */ + public function close() + { + $this->clear(); + $this->closed = true; + } + + /** + * Determines whether a document instance is managed in this DocumentManager. + * + * @param object $document + * @return boolean TRUE if this DocumentManager currently manages the given document, FALSE otherwise. + */ + public function contains($document) + { + if (!is_object($document)) { + throw new \InvalidArgumentException(gettype($document)); + } + return $this->unitOfWork->isScheduledForInsert($document) || + $this->unitOfWork->isInIdentityMap($document) && + ! $this->unitOfWork->isScheduledForDelete($document); + } + + /** + * Gets the Configuration used by the DocumentManager. + * + * @return Configuration + */ + public function getConfiguration() + { + return $this->config; + } + + public function getClassNameFromDiscriminatorValue(array $mapping, $value) + { + $discriminatorField = isset($mapping['discriminatorField']) ? $mapping['discriminatorField'] : '_doctrine_class_name'; + if (is_array($value) && isset($value[$discriminatorField])) { + $discriminatorValue = $value[$discriminatorField]; + return isset($mapping['discriminatorMap'][$discriminatorValue]) ? $mapping['discriminatorMap'][$discriminatorValue] : $discriminatorValue; + } else { + $class = $this->getClassMetadata($mapping['targetDocument']); + + // @TODO figure out how to remove this + if ($class->discriminatorField) { + if (isset($value[$class->discriminatorField['name']])) { + return $class->discriminatorMap[$value[$class->discriminatorField['name']]]; + } + } + } + return $mapping['targetDocument']; + } + + /** + * Returns a DBRef array for the supplied document. + * + * @param mixed $document A document object + * @param array $referenceMapping Mapping for the field the references the document + * + * @return array A DBRef array + */ + public function createDBRef($document, array $referenceMapping = null) + { + if (!is_object($document)) { + throw new \InvalidArgumentException('Cannot create a DBRef, the document is not an object'); + } + $className = get_class($document); + $class = $this->getClassMetadata($className); + $id = $this->unitOfWork->getDocumentIdentifier($document); + + if (isset($referenceMapping['simple']) && $referenceMapping['simple']) { + return $class->getDatabaseIdentifierValue($id); + } + + $dbRef = array( + $this->cmd . 'ref' => $class->getCollection(), + $this->cmd . 'id' => $class->getDatabaseIdentifierValue($id), + $this->cmd . 'db' => $this->getDocumentDatabase($className)->getName() + ); + + if ($class->discriminatorField) { + $dbRef[$class->discriminatorField['name']] = $class->discriminatorValue; + } + + // add a discriminator value if the referenced document is not mapped explicitly to a targetDocument + if ($referenceMapping && ! isset($referenceMapping['targetDocument'])) { + $discriminatorField = isset($referenceMapping['discriminatorField']) ? $referenceMapping['discriminatorField'] : '_doctrine_class_name'; + $discriminatorValue = isset($referenceMapping['discriminatorMap']) ? array_search($class->getName(), $referenceMapping['discriminatorMap']) : $class->getName(); + $dbRef[$discriminatorField] = $discriminatorValue; + } + + return $dbRef; + } + + /** + * Throws an exception if the DocumentManager is closed or currently not active. + * + * @throws MongoDBException If the DocumentManager is closed. + */ + private function errorIfClosed() + { + if ($this->closed) { + throw MongoDBException::documentManagerClosed(); + } + } + + /** + * Gets the filter collection. + * + * @return \Doctrine\ODM\MongoDB\Query\FilterCollection The active filter collection. + */ + public function getFilterCollection() + { + if (null === $this->filterCollection) { + $this->filterCollection = new FilterCollection($this); + } + + return $this->filterCollection; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentNotFoundException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentNotFoundException.php new file mode 100644 index 00000000..8c086454 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentNotFoundException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * Class for exception when encountering proxy object that has + * an identifier that does not exist in the database. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class DocumentNotFoundException extends MongoDBException +{ + public static function documentNotFound($className, $identifier) + { + return new self(sprintf('The "%s" document with identifier "%s" could not be found.', $className, $identifier)); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentRepository.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentRepository.php new file mode 100644 index 00000000..be821628 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/DocumentRepository.php @@ -0,0 +1,239 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\ODM\MongoDB\Mapping\MappingException; + +/** + * An DocumentRepository serves as a repository for documents with generic as well as + * business specific methods for retrieving documents. + * + * This class is designed for inheritance and users can subclass this class to + * write their own repositories with business-specific methods to locate documents. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class DocumentRepository implements ObjectRepository +{ + /** + * @var string + */ + protected $documentName; + + /** + * @var DocumentManager + */ + protected $dm; + + /** + * @var UnitOfWork + */ + protected $uow; + + /** + * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + protected $class; + + /** + * Initializes a new DocumentRepository. + * + * @param DocumentManager $dm The DocumentManager to use. + * @param UnitOfWork $uow The UnitOfWork to use. + * @param Mapping\ClassMetadata $classMetadata The class descriptor. + */ + public function __construct(DocumentManager $dm, UnitOfWork $uow, Mapping\ClassMetadata $class) + { + $this->documentName = $class->name; + $this->dm = $dm; + $this->uow = $uow; + $this->class = $class; + } + + /** + * Create a new Query\Builder instance that is prepopulated for this document name + * + * @return Query\Builder $qb + */ + public function createQueryBuilder() + { + return $this->dm->createQueryBuilder($this->documentName); + } + + /** + * Clears the repository, causing all managed documents to become detached. + */ + public function clear() + { + $this->dm->clear($this->class->rootDocumentName); + } + + /** + * Finds a document by its identifier + * + * @throws LockException + * @param string|object $id The identifier + * @param int $lockMode + * @param int $lockVersion + * @return object The document. + */ + public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) + { + if ($id === null) { + return; + } + if (is_array($id)) { + list($identifierFieldName) = $this->class->getIdentifierFieldNames(); + + if (!isset($id[$identifierFieldName])) { + throw MappingException::missingIdentifierField($this->documentName, $identifierFieldName); + } + + $id = $id[$identifierFieldName]; + } + + // Check identity map first + if ($document = $this->uow->tryGetById($id, $this->class->rootDocumentName)) { + if ($lockMode != LockMode::NONE) { + $this->dm->lock($document, $lockMode, $lockVersion); + } + + return $document; // Hit! + } + + if ($lockMode == LockMode::NONE) { + return $this->uow->getDocumentPersister($this->documentName)->load($id); + } else if ($lockMode == LockMode::OPTIMISTIC) { + if (!$this->class->isVersioned) { + throw LockException::notVersioned($this->documentName); + } + $document = $this->uow->getDocumentPersister($this->documentName)->load($id); + + $this->uow->lock($document, $lockMode, $lockVersion); + + return $document; + } else { + return $this->uow->getDocumentPersister($this->documentName)->load($id, null, array(), $lockMode); + } + } + + /** + * Finds all documents in the repository. + * + * @return array The entities. + */ + public function findAll() + { + return $this->findBy(array()); + } + + /** + * Finds documents by a set of criteria. + * + * @param array $criteria + * @return array + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + { + return $this->uow->getDocumentPersister($this->documentName)->loadAll($criteria, $orderBy, $limit, $offset); + } + + /** + * Finds a single document by a set of criteria. + * + * @param array $criteria + * @return object + */ + public function findOneBy(array $criteria) + { + return $this->uow->getDocumentPersister($this->documentName)->load($criteria); + } + + /** + * Adds support for magic finders. + * + * @return array|object The found document/documents. + * @throws BadMethodCallException If the method called is an invalid find* method + * or no find* method at all and therefore an invalid + * method call. + */ + public function __call($method, $arguments) + { + if (substr($method, 0, 6) == 'findBy') { + $by = substr($method, 6, strlen($method)); + $method = 'findBy'; + } elseif (substr($method, 0, 9) == 'findOneBy') { + $by = substr($method, 9, strlen($method)); + $method = 'findOneBy'; + } else { + throw new \BadMethodCallException( + "Undefined method '$method'. The method name must start with ". + "either findBy or findOneBy!" + ); + } + + if ( ! isset($arguments[0])) { + throw MongoDBException::findByRequiresParameter($method.$by); + } + + $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); + + if ($this->class->hasField($fieldName)) { + return $this->$method(array($fieldName => $arguments[0])); + } else { + throw MongoDBException::invalidFindByCall($this->documentName, $fieldName, $method.$by); + } + } + + /** + * @return string + */ + public function getDocumentName() + { + return $this->documentName; + } + + /** + * @return DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } + + /** + * @return Mapping\ClassMetadata + */ + public function getClassMetadata() + { + return $this->class; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->getDocumentName(); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/EagerCursor.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/EagerCursor.php new file mode 100644 index 00000000..78a9c906 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/EagerCursor.php @@ -0,0 +1,224 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\Cursor as BaseCursor; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * EagerCursor extends the default Doctrine\MongoDB\EagerCursor implementation. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class EagerCursor implements \Doctrine\MongoDB\Iterator +{ + /** + * Whether or not to hydrate the data to documents. + * + * @var boolean + */ + private $hydrate = true; + + /** + * Whether or not to refresh the data for documents that are already in the identity map. + * + * @var boolean + */ + private $refresh = false; + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $unitOfWork; + + /** + * The ClassMetadata instance. + * + * @var Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + private $class; + + /** + * The array of hints for the UnitOfWork. + * + * @var array + */ + private $hints = array(); + + /** + * @var object Doctrine\ODM\MongoDB\Cursor + */ + private $cursor; + + /** + * Array of data from Cursor to iterate over + * + * @var array + */ + private $data = array(); + + /** + * Whether or not the EagerCursor has been initialized + * + * @var bool $initialized + */ + private $initialized = false; + + /** @override */ + public function __construct(BaseCursor $cursor, UnitOfWork $uow, ClassMetadata $class) + { + $this->cursor = $cursor; + $this->unitOfWork = $uow; + $this->class = $class; + } + + /** + * Set hints to account for during reconstitution/lookup of the documents. + * + * @param array $hints + */ + public function setHints(array $hints) + { + $this->hints = $hints; + } + + /** + * Get hints to account for during reconstitution/lookup of the documents. + * + * @return array $hints + */ + public function getHints() + { + return $this->hints; + } + + public function getCursor() + { + return $this->cursor; + } + + public function isInitialized() + { + return $this->initialized; + } + + public function initialize() + { + if ($this->initialized === false) { + $this->data = $this->cursor->getBaseCursor()->toArray(); + } + $this->initialized = true; + } + + public function rewind() + { + $this->initialize(); + reset($this->data); + } + + public function current() + { + $this->initialize(); + $current = current($this->data); + if ($current && $this->hydrate) { + return $this->unitOfWork->getOrCreateDocument($this->class->name, $current, $this->hints); + } + return $current ? $current : null; + } + + public function key() + { + $this->initialize(); + return key($this->data); + } + + public function next() + { + $this->initialize(); + return next($this->data); + } + + public function valid() + { + $this->initialize(); + $key = key($this->data); + return ($key !== NULL && $key !== FALSE); + } + + public function count() + { + $this->initialize(); + return count($this->data); + } + + public function toArray() + { + $this->initialize(); + return iterator_to_array($this); + } + + public function getSingleResult() + { + $this->initialize(); + return $this->current(); + } + + /** + * Set whether to hydrate the documents to objects or not. + * + * @param boolean $bool + */ + public function hydrate($bool = true) + { + $this->hydrate = $bool; + return $this; + } + + /** + * Sets whether to refresh the documents data if it already exists in the identity map. + * + * @param boeolan $bool + */ + public function refresh($bool = true) + { + $this->refresh = $bool; + if ($this->refresh) { + $this->hints[Query::HINT_REFRESH] = true; + } else { + unset($this->hints[Query::HINT_REFRESH]); + } + return $this; + } + + /** @override */ + public function slaveOkay($okay = true) + { + if ($okay) { + $this->hints[Query::HINT_SLAVE_OKAY] = true; + } else { + unset($this->hints[Query::HINT_SLAVE_OKAY]); + } + $this->cursor->slaveOkay($okay); + return $this; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LifecycleEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LifecycleEventArgs.php new file mode 100644 index 00000000..63aba16c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LifecycleEventArgs.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\Common\EventArgs; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of documents. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class LifecycleEventArgs extends EventArgs +{ + /** + * @var DocumentManager + */ + private $dm; + + /** + * @var object + */ + private $document; + + public function __construct($document, $em) + { + $this->document = $document; + $this->dm = $em; + } + + public function getDocument() + { + return $this->document; + } + + /** + * @return DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 00000000..351ef741 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; +use Doctrine\ODM\MongoDB\DocumentManager; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class LoadClassMetadataEventArgs extends EventArgs +{ + private $classMetadata; + + private $dm; + + public function __construct(ClassMetadataInfo $classMetadata, DocumentManager $dm) + { + $this->classMetadata = $classMetadata; + $this->dm = $dm; + } + + public function getClassMetadata() + { + return $this->classMetadata; + } + + public function getDocumentManager() + { + return $this->dm; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php new file mode 100644 index 00000000..48a7e6c3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +/** + * Provides event arguments for the onClear event. + * + * @since 1.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Jonathan H. Wage + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var \Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * @var string + */ + private $documentClass; + + /** + * Constructor. + * + * @param \Doctrine\ODM\MongoDB\DocumentManager $dm + * @param string $documentClass Optional document class + */ + public function __construct($dm, $documentClass = null) + { + $this->em = $dm; + $this->documentClass = $documentClass; + } + + /** + * Retrieve associated DocumentManager. + * + * @return \Doctrine\ODM\MongoDB\DocumentManager + */ + public function getDocumentManager() + { + return $this->em; + } + + /** + * Name of the document class that is cleared, or empty if all are cleared. + * + * @return string + */ + public function getDocumentClass() + { + return $this->documentClass; + } + + /** + * Check if event clears all documents. + * + * @return bool + */ + public function clearsAllDocuments() + { + return ($this->documentClass === null); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnFlushEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnFlushEventArgs.php new file mode 100644 index 00000000..13f77933 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/OnFlushEventArgs.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\Common\EventArgs; + +/** + * Provides event arguments for the onFlush event. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class OnFlushEventArgs extends EventArgs +{ + /** + * @var DocumentManager + */ + private $dm; + + public function __construct($dm) + { + $this->dm = $dm; + } + + /** + * @return DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PostFlushEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PostFlushEventArgs.php new file mode 100644 index 00000000..bab36704 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PostFlushEventArgs.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\Common\EventArgs; + +/** + * Provides event arguments for the postFlush event. + * + * @since 1.0 + * @author Daniel Freudenberger + * @author Jonathan H. Wage + */ +class PostFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * Constructor. + * + * @param \Doctrine\ODM\MongoDB\DocumentManager $dm + */ + public function __construct(DocumentManager $dm) + { + $this->dm = $dm; + } + + /** + * Retrieve associated DocumentManager. + * + * @return \Doctrine\ODM\MongoDB\DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreFlushEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreFlushEventArgs.php new file mode 100644 index 00000000..9de25638 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreFlushEventArgs.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +/** + * Provides event arguments for the preFlush event. + * + * @since 1.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Jonathan H. Wage + */ +class PreFlushEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + public function __construct($dm) + { + $this->dm = $dm; + } + + /** + * @return Doctrine\ODM\MongoDB\DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php new file mode 100644 index 00000000..250b1a31 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\Common\EventArgs; + +/** + * PreLoad event arguments. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PreLoadEventArgs extends EventArgs +{ + /** + * @var DocumentManager + */ + private $dm; + + /** + * @var object + */ + private $document; + + /** + * @var array + */ + private $data; + + public function __construct($document, $em, array &$data) + { + $this->document = $document; + $this->dm = $em; + $this->data = $data; + } + + public function getDocument() + { + return $this->document; + } + + /** + * @return DocumentManager + */ + public function getDocumentManager() + { + return $this->dm; + } + + /** + * Get the array of data to be loaded and hydrated. + * + * @return array $data + */ + public function &getData() + { + return $this->data; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php new file mode 100644 index 00000000..a84753cb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Event; + +use Doctrine\ODM\MongoDB\DocumentManager; + +/** + * Class that holds event arguments for a preInsert/preUpdate event. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $documentChangeSet; + + /** + * + * @param object $document + * @param DocumentManager $dm + * @param array $changeSet + */ + public function __construct($document, $dm, array &$changeSet) + { + parent::__construct($document, $dm); + $this->documentChangeSet = &$changeSet; + } + + public function getDocumentChangeSet() + { + return $this->documentChangeSet; + } + + /** + * Field has a changeset? + * + * @return bool + */ + public function hasChangedField($field) + { + return isset($this->documentChangeSet[$field]); + } + + /** + * Get the old value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->documentChangeSet[$field][0]; + } + + /** + * Get the new value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->documentChangeSet[$field][1]; + } + + /** + * Set the new value of this field. + * + * @param string $field + * @param mixed $value + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->documentChangeSet[$field][1] = $value; + } + + private function assertValidField($field) + { + if ( ! isset($this->documentChangeSet[$field])) { + throw new \InvalidArgumentException( + "Field '".$field."' is not a valid field of the document ". + "'".get_class($this->getDocument())."' in PreInsertUpdateEventArgs." + ); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Events.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Events.php new file mode 100644 index 00000000..9a9366f4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Events.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * Container for all ODM events. + * + * This class cannot be instantiated. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +final class Events +{ + private function __construct() {} + + /** + * The preRemove event occurs for a given document before the respective + * DocumentManager remove operation for that document is executed. + * + * This is an document lifecycle event. + * + * @var string + */ + const preRemove = 'preRemove'; + + /** + * The postRemove event occurs for an document after the document has + * been deleted. It will be invoked after the database delete operations. + * + * This is an document lifecycle event. + * + * @var string + */ + const postRemove = 'postRemove'; + + /** + * The prePersist event occurs for a given document before the respective + * DocumentManager persist operation for that document is executed. + * + * This is an document lifecycle event. + * + * @var string + */ + const prePersist = 'prePersist'; + + /** + * The postPersist event occurs for an document after the document has + * been made persistent. It will be invoked after the database insert operations. + * Generated primary key values are available in the postPersist event. + * + * This is an document lifecycle event. + * + * @var string + */ + const postPersist = 'postPersist'; + + /** + * The preUpdate event occurs before the database update operations to + * document data. + * + * This is an document lifecycle event. + * + * @var string + */ + const preUpdate = 'preUpdate'; + + /** + * The postUpdate event occurs after the database update operations to + * document data. + * + * This is an document lifecycle event. + * + * @var string + */ + const postUpdate = 'postUpdate'; + + /** + * The preLoad event occurs for a document before the document has been loaded + * into the current DocumentManager from the database or before the refresh operation + * has been applied to it. + * + * This is a document lifecycle event. + * + * @var string + */ + const preLoad = 'preLoad'; + + /** + * The postLoad event occurs for a document after the document has been loaded + * into the current DocumentManager from the database or after the refresh operation + * has been applied to it. + * + * Note that the postLoad event occurs for an document before any associations have been + * initialized. Therefore it is not safe to access associations in a postLoad callback + * or event handler. + * + * This is a document lifecycle event. + * + * @var string + */ + const postLoad = 'postLoad'; + + /** + * The loadClassMetadata event occurs after the mapping metadata for a class + * has been loaded from a mapping source (annotations/xml/yaml). + * + * @var string + */ + const loadClassMetadata = 'loadClassMetadata'; + + /** + * The preFlush event occurs when the DocumentManager#flush() operation is invoked, + * but before any changes to managed documents have been calculated. This event is + * always raised right after DocumentManager#flush() call. + */ + const preFlush = 'preFlush'; + + /** + * The onFlush event occurs when the DocumentManager#flush() operation is invoked, + * after any changes to managed documents have been determined but before any + * actual database operations are executed. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the onFlush event is not raised. + * + * @var string + */ + const onFlush = 'onFlush'; + + /** + * The postFlush event occurs when the DocumentManager#flush() operation is invoked and + * after all actual database operations are executed successfully. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the postFlush event is not raised. The event won't be raised if an error occurs during the + * flush operation. + * + * @var string + */ + const postFlush = 'postFlush'; + + /** + * The onClear event occurs when the DocumentManager#clear() operation is invoked, + * after all references to documents have been removed from the unit of work. + * + * @var string + */ + const onClear = 'onClear'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorException.php new file mode 100644 index 00000000..702e7bc4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorException.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Hydrator; + +use Doctrine\ODM\MongoDB\MongoDBException; + +/** + * MongoDB ODM Hydrator Exception + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class HydratorException extends MongoDBException +{ + public static function hydratorDirectoryRequired() + { + return new self("You must configure a hydrator directory. See docs for details"); + } + + public static function hydratorNamespaceRequired() + { + return new self("You must configure a hydrator namespace. See docs for details"); + } + + public static function hydratorDirectoryMustExist() + { + return new self("You must create a hydrator directory specified"); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php new file mode 100644 index 00000000..00b84be6 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -0,0 +1,419 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Hydrator; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\Types\Type; +use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Events; +use Doctrine\Common\EventManager; +use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; +use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs; +use Doctrine\ODM\MongoDB\PersistentCollection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ODM\MongoDB\Proxy\Proxy; + +/** + * The HydratorFactory class is responsible for instantiating a correct hydrator + * type based on document's ClassMetadata + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class HydratorFactory +{ + /** + * The DocumentManager this factory is bound to. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $unitOfWork; + + /** + * The EventManager associated with this Hydrator + * + * @var Doctrine\Common\EventManager + */ + private $evm; + + /** + * Whether to automatically (re)generate hydrator classes. + * + * @var boolean + */ + private $autoGenerate; + + /** + * The namespace that contains all hydrator classes. + * + * @var string + */ + private $hydratorNamespace; + + /** + * The directory that contains all hydrator classes. + * + * @var string + */ + private $hydratorDir; + + /** + * Array of instantiated document hydrators. + * + * @var array + */ + private $hydrators = array(); + + /** + * Mongo command prefix + * @var string + */ + private $cmd; + + public function __construct(DocumentManager $dm, EventManager $evm, $hydratorDir, $hydratorNs, $autoGenerate, $cmd) + { + if ( ! $hydratorDir) { + throw HydratorException::hydratorDirectoryRequired(); + } + if ( ! $hydratorNs) { + throw HydratorException::hydratorNamespaceRequired(); + } + $this->dm = $dm; + $this->evm = $evm; + $this->hydratorDir = $hydratorDir; + $this->hydratorNamespace = $hydratorNs; + $this->autoGenerate = $autoGenerate; + $this->cmd = $cmd; + } + + /** + * Sets the UnitOfWork instance. + * + * @param UnitOfWork $uow + */ + public function setUnitOfWork(UnitOfWork $uow) + { + $this->unitOfWork = $uow; + } + + /** + * Gets the hydrator object for the given document class. + * + * @param string $className + * @return Doctrine\ODM\MongoDB\Hydrator\HydratorInterface $hydrator + */ + public function getHydratorFor($className) + { + if (isset($this->hydrators[$className])) { + return $this->hydrators[$className]; + } + $hydratorClassName = str_replace('\\', '', $className) . 'Hydrator'; + $fqn = $this->hydratorNamespace . '\\' . $hydratorClassName; + $class = $this->dm->getClassMetadata($className); + + if (! class_exists($fqn, false)) { + $fileName = $this->hydratorDir . DIRECTORY_SEPARATOR . $hydratorClassName . '.php'; + if ($this->autoGenerate) { + $this->generateHydratorClass($class, $hydratorClassName, $fileName); + } + require $fileName; + } + $this->hydrators[$className] = new $fqn($this->dm, $this->unitOfWork, $class); + return $this->hydrators[$className]; + } + + /** + * Generates hydrator classes for all given classes. + * + * @param array $classes The classes (ClassMetadata instances) for which to generate hydrators. + * @param string $toDir The target directory of the hydrator classes. If not specified, the + * directory configured on the Configuration of the DocumentManager used + * by this factory is used. + */ + public function generateHydratorClasses(array $classes, $toDir = null) + { + $hydratorDir = $toDir ?: $this->hydratorDir; + $hydratorDir = rtrim($hydratorDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + foreach ($classes as $class) { + $hydratorClassName = str_replace('\\', '', $class->name) . 'Hydrator'; + $hydratorFileName = $hydratorDir . $hydratorClassName . '.php'; + $this->generateHydratorClass($class, $hydratorClassName, $hydratorFileName); + } + } + + private function generateHydratorClass(ClassMetadata $class, $hydratorClassName, $fileName) + { + $code = ''; + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['alsoLoadFields'])) { + foreach ($mapping['alsoLoadFields'] as $name) { + $code .= sprintf(<<class->reflFields['%2\$s']->setValue(\$document, clone \$return); + \$hydratedData['%2\$s'] = \$return; + } + +EOF + , + $mapping['name'], + $mapping['fieldName'], + Type::getType($mapping['type'])->closureToPHP() + ); + + + } elseif ( ! isset($mapping['association'])) { + $code .= sprintf(<<class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + } + +EOF + , + $mapping['name'], + $mapping['fieldName'], + Type::getType($mapping['type'])->closureToPHP() + ); + } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { + $code .= sprintf(<<class->fieldMappings['%2\$s']['simple']) && \$this->class->fieldMappings['%2\$s']['simple']) { + \$className = \$this->class->fieldMappings['%2\$s']['targetDocument']; + \$mongoId = \$reference; + } else { + \$className = \$this->dm->getClassNameFromDiscriminatorValue(\$this->class->fieldMappings['%2\$s'], \$reference); + \$mongoId = \$reference['\$id']; + } + \$targetMetadata = \$this->dm->getClassMetadata(\$className); + \$id = \$targetMetadata->getPHPIdentifierValue(\$mongoId); + \$return = \$this->dm->getReference(\$className, \$id); + \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + } + +EOF + , + $mapping['name'], + $mapping['fieldName'] + ); + } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isInverseSide']) { + if (isset($mapping['repositoryMethod']) && $mapping['repositoryMethod']) { + $code .= sprintf(<<class->fieldMappings['%2\$s']['targetDocument']; + \$return = \$this->dm->getRepository(\$className)->%3\$s(\$document); + \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + +EOF + , + $mapping['name'], + $mapping['fieldName'], + $mapping['repositoryMethod'] + ); + } else { + $code .= sprintf(<<class->fieldMappings['%2\$s']; + \$className = \$mapping['targetDocument']; + \$targetClass = \$this->dm->getClassMetadata(\$mapping['targetDocument']); + \$mappedByMapping = \$targetClass->fieldMappings[\$mapping['mappedBy']]; + \$mappedByFieldName = isset(\$mappedByMapping['simple']) && \$mappedByMapping['simple'] ? \$mapping['mappedBy'] : \$mapping['mappedBy'].'.\$id'; + \$criteria = array_merge( + array(\$mappedByFieldName => \$data['_id']), + isset(\$this->class->fieldMappings['%2\$s']['criteria']) ? \$this->class->fieldMappings['%2\$s']['criteria'] : array() + ); + \$sort = isset(\$this->class->fieldMappings['%2\$s']['sort']) ? \$this->class->fieldMappings['%2\$s']['sort'] : array(); + \$return = \$this->unitOfWork->getDocumentPersister(\$className)->load(\$criteria, null, array(), 0, \$sort); + \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + +EOF + , + $mapping['name'], + $mapping['fieldName'] + ); + } + } elseif ($mapping['association'] === ClassMetadata::REFERENCE_MANY || $mapping['association'] === ClassMetadata::EMBED_MANY) { + $code .= sprintf(<<dm, \$this->unitOfWork, '$'); + \$return->setHints(\$hints); + \$return->setOwner(\$document, \$this->class->fieldMappings['%2\$s']); + \$return->setInitialized(false); + if (\$mongoData) { + \$return->setMongoData(\$mongoData); + } + \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + +EOF + , + $mapping['name'], + $mapping['fieldName'] + ); + } elseif ($mapping['association'] === ClassMetadata::EMBED_ONE) { + $code .= sprintf(<<dm->getClassNameFromDiscriminatorValue(\$this->class->fieldMappings['%2\$s'], \$embeddedDocument); + \$embeddedMetadata = \$this->dm->getClassMetadata(\$className); + \$return = \$embeddedMetadata->newInstance(); + + \$embeddedData = \$this->dm->getHydratorFactory()->hydrate(\$return, \$embeddedDocument, \$hints); + \$this->unitOfWork->registerManaged(\$return, null, \$embeddedData); + \$this->unitOfWork->setParentAssociation(\$return, \$this->class->fieldMappings['%2\$s'], \$document, '%1\$s'); + + \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); + \$hydratedData['%2\$s'] = \$return; + } + +EOF + , + $mapping['name'], + $mapping['fieldName'] + ); + } + } + + $className = $class->name; + $namespace = $this->hydratorNamespace; + $code = sprintf(<<dm = \$dm; + \$this->unitOfWork = \$uow; + \$this->class = \$class; + } + + public function hydrate(\$document, \$data, array \$hints = array()) + { + \$hydratedData = array(); +%s return \$hydratedData; + } +} +EOF + , + $code + ); + + file_put_contents($fileName, $code); + } + + /** + * Hydrate array of MongoDB document data into the given document object. + * + * @param object $document The document object to hydrate the data into. + * @param array $data The array of document data. + * @param array $hints Any hints to account for during reconstitution/lookup of the document. + * @return array $values The array of hydrated values. + */ + public function hydrate($document, $data, array $hints = array()) + { + $metadata = $this->dm->getClassMetadata(get_class($document)); + // Invoke preLoad lifecycle events and listeners + if (isset($metadata->lifecycleCallbacks[Events::preLoad])) { + $args = array(&$data); + $metadata->invokeLifecycleCallbacks(Events::preLoad, $document, $args); + } + if ($this->evm->hasListeners(Events::preLoad)) { + $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($document, $this->dm, $data)); + } + + // Use the alsoLoadMethods on the document object to transform the data before hydration + if (isset($metadata->alsoLoadMethods)) { + foreach ($metadata->alsoLoadMethods as $fieldName => $method) { + if (isset($data[$fieldName])) { + $document->$method($data[$fieldName]); + } + } + } + + $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints); + if ($document instanceof Proxy) { + $document->__isInitialized__ = true; + } + + // Invoke the postLoad lifecycle callbacks and listeners + if (isset($metadata->lifecycleCallbacks[Events::postLoad])) { + $metadata->invokeLifecycleCallbacks(Events::postLoad, $document); + } + if ($this->evm->hasListeners(Events::postLoad)) { + $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($document, $this->dm)); + } + + return $data; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php new file mode 100644 index 00000000..2d63b9e3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Hydrator; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * The HydratorInterface defines methods all hydrator need to implement + * + * @since 1.0 + * @author Jonathan H. Wage + */ +interface HydratorInterface +{ + /** + * Hydrate array of MongoDB document data into the given document object. + * + * @param object $document The document object to hydrate the data into. + * @param array $data The array of document data. + * @param array $hints Any hints to account for during reconstitution/lookup of the document. + * @return array $values The array of hydrated values. + */ + function hydrate($document, $data, array $hints = array()); +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AbstractIdGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AbstractIdGenerator.php new file mode 100644 index 00000000..b6699eba --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AbstractIdGenerator.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Id; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * AbstractIdGenerator + * + * @since 1.0 + * @author Jonathan H. Wage + */ +abstract class AbstractIdGenerator +{ + /** + * Generates an identifier for a document. + * + * @param Doctrine\ODM\MongoDB\DocumentManager $document + * @return mixed + */ + abstract public function generate(DocumentManager $dm, $document); +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AlnumGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AlnumGenerator.php new file mode 100644 index 00000000..9133f648 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AlnumGenerator.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Id; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * AlnumGenerator is responsible for generating cased alpha-numeric unique identifiers. + * It extends IncrementGenerator in order to ensure uniqueness even with short strings. + * + * "Awkward safe mode" avoids combinations that results in 'dirty' words by removing + * the vowels from chars index + * + * A minimum identifier length can be enforced by setting a numeric value to the "pad" option + * (with only 6 chars you will have more than 56 billion unique id's, 15 billion in 'awkward safe mode') + * + * The character set used for ID generation can be explicitly set with the "chars" option (e.g. base36, etc.) + * + * @since 1.0 + * @author Frederik Eychenié + */ +class AlnumGenerator extends IncrementGenerator +{ + + protected $pad = null; + + protected $awkwardSafeMode = false; + + protected $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + protected $awkwardSafeChars = '0123456789BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz'; + + /** + * Set padding on generated id + * + * @param int $pad + */ + public function setPad($pad) + { + $this->pad = intval($pad); + } + + /** + * Enable awkwardSafeMode character set + * + * @param bool $awkwardSafeMode + */ + public function setAwkwardSafeMode($awkwardSafeMode = false) + { + $this->awkwardSafeMode = $awkwardSafeMode; + } + + /** + * Set the character set used for ID generation + * + * @param string $chars ID character set + */ + public function setChars($chars) + { + $this->chars = $chars; + } + + /** @inheritDoc */ + public function generate(DocumentManager $dm, $document) + { + $id = parent::generate($dm, $document); + $index = $this->awkwardSafeMode ? $this->awkwardSafeChars : $this->chars; + $base = strlen($index); + + $out = ""; + do { + $out = $index[bcmod($id, $base)] . $out; + $id = bcdiv($id, $base); + } while (bccomp($id, 0) == 1); + + if (is_numeric($this->pad)) { + $out = str_pad($out, $this->pad, "0", STR_PAD_LEFT); + } + + return $out; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AutoGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AutoGenerator.php new file mode 100644 index 00000000..8cc37740 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/AutoGenerator.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Id; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * AutoGenerator generates a native MongoId + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class AutoGenerator extends AbstractIdGenerator +{ + /** @inheritDoc */ + public function generate(DocumentManager $dm, $document) + { + return new \MongoId(); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php new file mode 100644 index 00000000..570a7e61 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Id; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * IncrementGenerator is responsible for generating auto increment identifiers. It uses + * a collection and generates the next id by using $inc on a field named "current_id". + * + * The 'collection' property determines which collection name is used to store the + * id values. If not specified it defaults to 'doctrine_increment_ids'. + * + * The 'key' property determines the document ID used to store the id values in the + * collection. If not specified it defaults to the name of the collection for the + * document. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class IncrementGenerator extends AbstractIdGenerator +{ + protected $collection = null; + protected $key = null; + + public function setCollection($collection) + { + $this->collection = $collection; + } + + public function setKey($key) + { + $this->key = $key; + } + + /** @inheritDoc */ + public function generate(DocumentManager $dm, $document) + { + $className = get_class($document); + $db = $dm->getDocumentDatabase($className); + + $coll = $this->collection ?: 'doctrine_increment_ids'; + $key = $this->key ?: $dm->getDocumentCollection($className)->getName(); + + $query = array('_id' => $key); + $newObj = array('$inc' => array('current_id' => 1)); + + $command = array(); + $command['findandmodify'] = $coll; + $command['query'] = $query; + $command['update'] = $newObj; + $command['upsert'] = true; + $command['new'] = true; + $result = $db->command($command); + return $result['value']['current_id']; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/UuidGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/UuidGenerator.php new file mode 100644 index 00000000..996fcf38 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Id/UuidGenerator.php @@ -0,0 +1,158 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Id; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * UuidGenerator generates a uuid for the id. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class UuidGenerator extends AbstractIdGenerator +{ + /** + * A unique environment value to salt each GUID with. + * + * @var string + */ + protected $salt = null; + + /** + * Used to set the salt that will be applied to each id + * + * @param string $salt The sale to use + */ + public function setSalt($salt) + { + $this->salt = $salt; + } + + /** + * Returns the current salt value + * + * @return string $salt The current salt + */ + public function getSalt() + { + return $this->salt; + } + + /** + * Checks that a given string is a valid uuid. + * + * @param string $uuid The string to check. + * @return boolean + */ + public function isValid($guid) + { + return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $guid) === 1; + } + + /** + * Generates a new GUID + * + * @return string + */ + public function generate(DocumentManager $dm, $document) + { + $guid = $this->generateV4(); + return $this->generateV5($guid, $this->salt ?: php_uname('n')); + } + + /** + * Generates a v4 GUID + * + * @return string + */ + public function generateV4() + { + return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x', + // 32 bits for "time_low" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + + // 16 bits for "time_mid" + mt_rand(0, 0xffff), + + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 4 + mt_rand(0, 0x0fff) | 0x4000, + + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + mt_rand(0, 0x3fff) | 0x8000, + + // 48 bits for "node" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); + } + + /** + * Generates a v5 GUID + * + * @param string $namespace The GUID to seed with + * @param string $salt The string to salt this new UUID with + * @return string + */ + public function generateV5($namespace, $salt) + { + if (!$this->isValid($namespace)) { + throw new \Exception('Provided $namespace is invalid: ' . $namespace); + } + + // Get hexadecimal components of namespace + $nhex = str_replace(array('-','{','}'), '', $namespace); + + // Binary Value + $nstr = ''; + + // Convert Namespace UUID to bits + for ($i = 0; $i < strlen($nhex); $i += 2) { + $nstr .= chr(hexdec($nhex[$i] . $nhex[$i+1])); + } + + // Calculate hash value + $hash = sha1($nstr . $salt); + + $guid = sprintf('%08s%04s%04x%04x%12s', + // 32 bits for "time_low" + substr($hash, 0, 8), + + // 16 bits for "time_mid" + substr($hash, 8, 4), + + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 3 + (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000, + + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000, + + // 48 bits for "node" + substr($hash, 20, 12) + ); + + return $guid; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Internal/CommitOrderCalculator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Internal/CommitOrderCalculator.php new file mode 100644 index 00000000..13081487 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Internal/CommitOrderCalculator.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Internal; + +/** + * The CommitOrderCalculator is used by the UnitOfWork to sort out the + * correct order in which changes to documents need to be persisted. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class CommitOrderCalculator +{ + const NOT_VISITED = 1; + const IN_PROGRESS = 2; + const VISITED = 3; + + private $nodeStates = array(); + private $classes = array(); // The nodes to sort + private $relatedClasses = array(); + private $sorted = array(); + + /** + * Clears the current graph. + * + * @return void + */ + public function clear() + { + $this->classes = + $this->relatedClasses = array(); + } + + /** + * Gets a valid commit order for all current nodes. + * + * Uses a depth-first search (DFS) to traverse the graph. + * The desired topological sorting is the reverse postorder of these searches. + * + * @return array The list of ordered classes. + */ + public function getCommitOrder() + { + // Check whether we need to do anything. 0 or 1 node is easy. + $nodeCount = count($this->classes); + if ($nodeCount === 0) { + return array(); + } + + if ($nodeCount === 1) { + return array_values($this->classes); + } + + // Init + foreach ($this->classes as $node) { + $this->nodeStates[$node->name] = self::NOT_VISITED; + } + + // Go + foreach ($this->classes as $node) { + if ($this->nodeStates[$node->name] == self::NOT_VISITED) { + $this->visitNode($node); + } + } + + $sorted = array_reverse($this->sorted); + + $this->sorted = $this->nodeStates = array(); + + return $sorted; + } + + private function visitNode($node) + { + $this->nodeStates[$node->name] = self::IN_PROGRESS; + + if (isset($this->relatedClasses[$node->name])) { + foreach ($this->relatedClasses[$node->name] as $relatedNode) { + if ($this->nodeStates[$relatedNode->name] == self::NOT_VISITED) { + $this->visitNode($relatedNode); + } + } + } + + $this->nodeStates[$node->name] = self::VISITED; + $this->sorted[] = $node; + } + + public function addDependency($fromClass, $toClass) + { + $this->relatedClasses[$fromClass->name][] = $toClass; + } + + public function hasDependency($fromClass, $toClass) + { + if ( ! isset($this->relatedClasses[$fromClass->name])) { + return false; + } + + return in_array($toClass, $this->relatedClasses[$fromClass->name]); + } + + public function hasClass($className) + { + return isset($this->classes[$className]); + } + + public function addClass($class) + { + $this->classes[$class->name] = $class; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockException.php new file mode 100644 index 00000000..0034c32a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockException.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * LockException + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Jonathan H. Wage + * @since 1.0 + */ +class LockException extends MongoDBException +{ + private $document; + + public function __construct($msg, $document = null) + { + parent::__construct($msg); + $this->document = $document; + } + + /** + * Gets the document that caused the exception. + * + * @return object + */ + public function getDocument() + { + return $this->document; + } + + public static function lockFailed($document) + { + return new self('A lock failed on a document.', $document); + } + + public static function lockFailedVersionMissmatch($document, $expectedLockVersion, $actualLockVersion) + { + return new self('The optimistic lock failed, version ' . $expectedLockVersion . ' was expected, but is actually '.$actualLockVersion, $document); + } + + public static function notVersioned($documentName) + { + return new self('Document ' . $documentName . ' is not versioned.'); + } + + public static function invalidLockFieldType($type) + { + return new self('Invalid lock field type '.$type.'. Lock field must be int.'); + } + + public static function invalidVersionFieldType($type) + { + return new self('Invalid version field type '.$type.'. Version field must be int or date.'); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockMode.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockMode.php new file mode 100644 index 00000000..2090452e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LockMode.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * Contains all MongoDB ODM LockModes + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + * @author Jonathan H. Wage + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + final private function __construct() { } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LoggableCursor.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LoggableCursor.php new file mode 100644 index 00000000..e950d89f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/LoggableCursor.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\MongoDB\Collection; +use Doctrine\MongoDB\Connection; +use Doctrine\MongoDB\Cursor as BaseCursor; +use Doctrine\MongoDB\Loggable; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * LoggableCursor adds logging to the default ODM cursor. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Kris Wallsmith + */ +class LoggableCursor extends Cursor implements Loggable +{ + /** + * A callable for logging statements. + * + * @var mixed + */ + protected $loggerCallable; + + /** @override */ + public function __construct(Connection $connection, Collection $collection, UnitOfWork $uow, ClassMetadata $class, BaseCursor $baseCursor, array $query = array(), array $fields = array(), $numRetries = 0, $loggerCallable) + { + parent::__construct($connection, $collection, $uow, $class, $baseCursor, $query, $fields, $numRetries); + $this->loggerCallable = $loggerCallable; + } + + public function sort($fields) + { + $this->log(array( + 'sort' => true, + 'sortFields' => $fields + )); + + return parent::sort($fields); + } + + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + public function log(array $log) + { + $log['query'] = $this->query; + $log['fields'] = $this->fields; + call_user_func_array($this->loggerCallable, array($log)); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractDocument.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractDocument.php new file mode 100644 index 00000000..10635a25 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractDocument.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +abstract class AbstractDocument extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php new file mode 100644 index 00000000..c8c2d0e3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +abstract class AbstractField extends Annotation +{ + public $name; + public $type = 'string'; + public $nullable = false; + public $options = array(); + public $strategy; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php new file mode 100644 index 00000000..d5e994e4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +abstract class AbstractIndex extends Annotation +{ + public $keys = array(); + public $name; + public $dropDups; + public $background; + public $safe; + public $expireAfterSeconds; + public $order; + public $unique = false; + public $sparse = false; + public $options = array(); +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php new file mode 100644 index 00000000..e74ce074 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class AlsoLoad extends Annotation +{ + public $name; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Bin.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Bin.php new file mode 100644 index 00000000..220475e0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Bin.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Bin extends AbstractField +{ + public $type = 'bin'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinCustom.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinCustom.php new file mode 100644 index 00000000..bc35e73f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinCustom.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class BinCustom extends AbstractField +{ + public $type = 'bin_custom'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinFunc.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinFunc.php new file mode 100644 index 00000000..fdc5f984 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinFunc.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class BinFunc extends AbstractField +{ + public $type = 'bin_func'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinMD5.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinMD5.php new file mode 100644 index 00000000..1ba9df92 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinMD5.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class BinMD5 extends AbstractField +{ + public $type = 'bin_md5'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinUUID.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinUUID.php new file mode 100644 index 00000000..0440e049 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/BinUUID.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class BinUUID extends AbstractField +{ + public $type = 'bin_uuid'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Boolean.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Boolean.php new file mode 100644 index 00000000..08d833fb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Boolean.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Boolean extends AbstractField +{ + public $type = 'boolean'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ChangeTrackingPolicy.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ChangeTrackingPolicy.php new file mode 100644 index 00000000..b2560e69 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ChangeTrackingPolicy.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class ChangeTrackingPolicy extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Collection.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Collection.php new file mode 100644 index 00000000..bdce45e5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Collection.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Collection extends AbstractField +{ + public $type = 'collection'; + public $strategy = 'pushAll'; // pushAll, set +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Date.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Date.php new file mode 100644 index 00000000..026b0642 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Date.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Date extends AbstractField +{ + public $type = 'date'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorField.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorField.php new file mode 100644 index 00000000..1a7410e9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorField.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class DiscriminatorField extends Annotation +{ + public $name; + public $fieldName; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorMap.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorMap.php new file mode 100644 index 00000000..64a77e99 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorMap.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class DiscriminatorMap extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorValue.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorValue.php new file mode 100644 index 00000000..d66ec29a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DiscriminatorValue.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class DiscriminatorValue extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Distance.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Distance.php new file mode 100644 index 00000000..9c9539c1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Distance.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Distance extends AbstractField +{ + public $distance = true; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DoctrineAnnotations.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DoctrineAnnotations.php new file mode 100644 index 00000000..0aa8784f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/DoctrineAnnotations.php @@ -0,0 +1,74 @@ +. + */ + +require_once __DIR__ . '/AbstractDocument.php'; +require_once __DIR__ . '/Document.php'; +require_once __DIR__ . '/EmbeddedDocument.php'; +require_once __DIR__ . '/MappedSuperclass.php'; +require_once __DIR__ . '/Inheritance.php'; +require_once __DIR__ . '/InheritanceType.php'; +require_once __DIR__ . '/DiscriminatorField.php'; +require_once __DIR__ . '/DiscriminatorMap.php'; +require_once __DIR__ . '/DiscriminatorValue.php'; +require_once __DIR__ . '/Indexes.php'; +require_once __DIR__ . '/AbstractIndex.php'; +require_once __DIR__ . '/Index.php'; +require_once __DIR__ . '/UniqueIndex.php'; +require_once __DIR__ . '/Version.php'; +require_once __DIR__ . '/Lock.php'; +require_once __DIR__ . '/AbstractField.php'; +require_once __DIR__ . '/Field.php'; +require_once __DIR__ . '/Id.php'; +require_once __DIR__ . '/Hash.php'; +require_once __DIR__ . '/Boolean.php'; +require_once __DIR__ . '/Int.php'; +require_once __DIR__ . '/Float.php'; +require_once __DIR__ . '/String.php'; +require_once __DIR__ . '/Date.php'; +require_once __DIR__ . '/Key.php'; +require_once __DIR__ . '/Timestamp.php'; +require_once __DIR__ . '/Bin.php'; +require_once __DIR__ . '/BinFunc.php'; +require_once __DIR__ . '/BinUUID.php'; +require_once __DIR__ . '/BinMD5.php'; +require_once __DIR__ . '/BinCustom.php'; +require_once __DIR__ . '/File.php'; +require_once __DIR__ . '/Increment.php'; +require_once __DIR__ . '/ObjectId.php'; +require_once __DIR__ . '/Collection.php'; +require_once __DIR__ . '/Raw.php'; +require_once __DIR__ . '/EmbedOne.php'; +require_once __DIR__ . '/EmbedMany.php'; +require_once __DIR__ . '/ReferenceOne.php'; +require_once __DIR__ . '/ReferenceMany.php'; +require_once __DIR__ . '/NotSaved.php'; +require_once __DIR__ . '/Distance.php'; +require_once __DIR__ . '/AlsoLoad.php'; +require_once __DIR__ . '/ChangeTrackingPolicy.php'; +require_once __DIR__ . '/PrePersist.php'; +require_once __DIR__ . '/PostPersist.php'; +require_once __DIR__ . '/PreUpdate.php'; +require_once __DIR__ . '/PostUpdate.php'; +require_once __DIR__ . '/PreRemove.php'; +require_once __DIR__ . '/PostRemove.php'; +require_once __DIR__ . '/PreLoad.php'; +require_once __DIR__ . '/PostLoad.php'; +require_once __DIR__ . '/PreFlush.php'; diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php new file mode 100644 index 00000000..cf536475 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Document extends AbstractDocument +{ + public $db; + public $collection; + public $repositoryClass; + public $indexes = array(); + public $requireIndexes = false; + public $slaveOkay; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php new file mode 100644 index 00000000..8b10bd26 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class EmbedMany extends AbstractField +{ + public $type = 'many'; + public $embedded = true; + public $targetDocument; + public $discriminatorField; + public $discriminatorMap; + public $strategy = 'pushAll'; // pushAll, set +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php new file mode 100644 index 00000000..80b6c7ad --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class EmbedOne extends AbstractField +{ + public $type = 'one'; + public $embedded = true; + public $targetDocument; + public $discriminatorField; + public $discriminatorMap; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbeddedDocument.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbeddedDocument.php new file mode 100644 index 00000000..8a625d17 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbeddedDocument.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class EmbeddedDocument extends AbstractDocument +{ + public $indexes = array(); +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php new file mode 100644 index 00000000..df79f9ca --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Field extends AbstractField +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php new file mode 100644 index 00000000..efe486e1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class File extends AbstractField +{ + public $type = 'file'; + public $file = true; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Float.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Float.php new file mode 100644 index 00000000..a68e0753 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Float.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Float extends AbstractField +{ + public $type = 'float'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Hash.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Hash.php new file mode 100644 index 00000000..12157e85 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Hash.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Hash extends AbstractField +{ + public $type = 'hash'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php new file mode 100644 index 00000000..53a90ff5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Id extends AbstractField +{ + public $id = true; + public $type = 'id'; + public $strategy = 'auto'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Increment.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Increment.php new file mode 100644 index 00000000..d55297cb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Increment.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Increment extends AbstractField +{ + public $type = 'increment'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Index.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Index.php new file mode 100644 index 00000000..62467338 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Index.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Index extends AbstractIndex +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Indexes.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Indexes.php new file mode 100644 index 00000000..2802c31b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Indexes.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class Indexes extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Inheritance.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Inheritance.php new file mode 100644 index 00000000..06ca8a95 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Inheritance.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class Inheritance extends Annotation +{ + public $type = 'NONE'; + public $discriminatorMap = array(); + public $discriminatorField; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/InheritanceType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/InheritanceType.php new file mode 100644 index 00000000..1e9525cc --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/InheritanceType.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class InheritanceType extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Int.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Int.php new file mode 100644 index 00000000..b90436b8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Int.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Int extends AbstractField +{ + public $type = 'int'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Key.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Key.php new file mode 100644 index 00000000..a08b797b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Key.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Key extends AbstractField +{ + public $type = 'key'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Lock.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Lock.php new file mode 100644 index 00000000..ed8391d0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Lock.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class Lock extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/MappedSuperclass.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/MappedSuperclass.php new file mode 100644 index 00000000..1e5b855c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/MappedSuperclass.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class MappedSuperclass extends AbstractDocument +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/NotSaved.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/NotSaved.php new file mode 100644 index 00000000..18e38d0a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/NotSaved.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class NotSaved extends AbstractField +{ + public $notSaved = true; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ObjectId.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ObjectId.php new file mode 100644 index 00000000..5c00bce2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ObjectId.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class ObjectId extends AbstractField +{ + public $type = 'object_id'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostLoad.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostLoad.php new file mode 100644 index 00000000..6518287d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostLoad.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PostLoad extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostPersist.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostPersist.php new file mode 100644 index 00000000..8843a127 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostPersist.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PostPersist extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostRemove.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostRemove.php new file mode 100644 index 00000000..e361b699 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostRemove.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PostRemove extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostUpdate.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostUpdate.php new file mode 100644 index 00000000..359ca4b1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PostUpdate.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PostUpdate extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreFlush.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreFlush.php new file mode 100644 index 00000000..acd17f55 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreFlush.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PreFlush extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreLoad.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreLoad.php new file mode 100644 index 00000000..2ded1f14 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreLoad.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PreLoad extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PrePersist.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PrePersist.php new file mode 100644 index 00000000..4af87963 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PrePersist.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PrePersist extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreRemove.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreRemove.php new file mode 100644 index 00000000..792ce236 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreRemove.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PreRemove extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreUpdate.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreUpdate.php new file mode 100644 index 00000000..cf61077e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/PreUpdate.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class PreUpdate extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Raw.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Raw.php new file mode 100644 index 00000000..58503dcf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Raw.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Raw extends AbstractField +{ + public $type = 'raw'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php new file mode 100644 index 00000000..193c708a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class ReferenceMany extends AbstractField +{ + public $type = 'many'; + public $reference = true; + public $simple = false; + public $targetDocument; + public $discriminatorField; + public $discriminatorMap; + public $cascade; + public $inversedBy; + public $mappedBy; + public $repositoryMethod; + public $sort = array(); + public $criteria = array(); + public $limit; + public $skip; + public $strategy = 'pushAll'; // pushAll, set +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php new file mode 100644 index 00000000..b172b09b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class ReferenceOne extends AbstractField +{ + public $type = 'one'; + public $reference = true; + public $simple = false; + public $targetDocument; + public $discriminatorField; + public $discriminatorMap; + public $cascade; + public $inversedBy; + public $mappedBy; + public $repositoryMethod; + public $sort = array(); + public $criteria = array(); + public $limit; + public $skip; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/String.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/String.php new file mode 100644 index 00000000..86fb3ec8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/String.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class String extends AbstractField +{ + public $type = 'string'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Timestamp.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Timestamp.php new file mode 100644 index 00000000..1cde3dff --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Timestamp.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class Timestamp extends AbstractField +{ + public $type = 'timestamp'; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php new file mode 100644 index 00000000..cbbcc368 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +/** @Annotation */ +final class UniqueIndex extends AbstractIndex +{ + public $unique = true; +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Version.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Version.php new file mode 100644 index 00000000..e9c4dc18 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Version.php @@ -0,0 +1,27 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + +/** @Annotation */ +final class Version extends Annotation +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php new file mode 100644 index 00000000..0c4cb23c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -0,0 +1,202 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping; + +use Doctrine\ODM\MongoDB\LockException; + +/** + * A ClassMetadata instance holds all the object-document mapping metadata + * of a document and it's references. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class ClassMetadata extends ClassMetadataInfo +{ + /** + * The ReflectionProperty instances of the mapped class. + * + * @var array + */ + public $reflFields = array(); + + /** + * The prototype from which new instances of the mapped class are created. + * + * @var object + */ + private $prototype; + + /** + * Initializes a new ClassMetadata instance that will hold the object-document mapping + * metadata of the class with the given name. + * + * @param string $documentName The name of the document class the new instance is used for. + */ + public function __construct($documentName) + { + parent::__construct($documentName); + $this->reflClass = new \ReflectionClass($documentName); + $this->namespace = $this->reflClass->getNamespaceName(); + $this->setCollection($this->reflClass->getShortName()); + } + + /** + * Map a field. + * + * @param array $mapping The mapping information. + */ + public function mapField(array $mapping) + { + $mapping = parent::mapField($mapping); + + if ($this->reflClass->hasProperty($mapping['fieldName'])) { + $reflProp = $this->reflClass->getProperty($mapping['fieldName']); + $reflProp->setAccessible(true); + $this->reflFields[$mapping['fieldName']] = $reflProp; + } + } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * Parts that are also NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + // This metadata is always serialized/cached. + $serialized = array( + 'fieldMappings', + 'identifier', + 'name', + 'namespace', // TODO: REMOVE + 'db', + 'collection', + 'rootDocumentName', + 'generatorType', + 'generatorOptions', + 'idGenerator', + 'indexes' + ); + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorField'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->isEmbeddedDocument) { + $serialized[] = 'isEmbeddedDocument'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->file) { + $serialized[] = 'file'; + } + + if ($this->slaveOkay) { + $serialized[] = 'slaveOkay'; + } + + if ($this->distance) { + $serialized[] = 'distance'; + } + + return $serialized; + } + + /** + * Restores some state that can not be serialized/unserialized. + * + * @return void + */ + public function __wakeup() + { + // Restore ReflectionClass and properties + $this->reflClass = new \ReflectionClass($this->name); + + foreach ($this->fieldMappings as $field => $mapping) { + if (isset($mapping['declared'])) { + $reflField = new \ReflectionProperty($mapping['declared'], $field); + } else { + $reflField = $this->reflClass->getProperty($field); + } + $reflField->setAccessible(true); + $this->reflFields[$field] = $reflField; + } + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + if ($this->prototype === null) { + $this->prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); + } + + return clone $this->prototype; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php new file mode 100644 index 00000000..0ab434bc --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php @@ -0,0 +1,307 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping; + +use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; +use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Configuration; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Events; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping informations of a class which describes how a class should be mapped + * to a document database. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class ClassMetadataFactory extends AbstractClassMetadataFactory +{ + protected $cacheSalt = "\$MONGODBODMCLASSMETADATA"; + + /** @var DocumentManager The DocumentManager instance */ + private $dm; + + /** @var Configuration The Configuration instance */ + private $config; + + /** @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver The used metadata driver. */ + private $driver; + + /** @var \Doctrine\Common\EventManager The event manager instance */ + private $evm; + + /** + * Sets the DocumentManager instance for this class. + * + * @param DocumentManager $dm The DocumentManager instance + */ + public function setDocumentManager(DocumentManager $dm) + { + $this->dm = $dm; + } + + /** + * Sets the Configuration instance + * + * @param Configuration $config + */ + public function setConfiguration(Configuration $config) + { + $this->config = $config; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + */ + protected function initialize() + { + $this->driver = $this->config->getMetadataDriverImpl(); + $this->evm = $this->dm->getEventManager(); + $this->initialized = true; + } + + /** + * {@inheritDoc} + */ + protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) + { + return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + /** + * {@inheritDoc} + */ + protected function getDriver() + { + return $this->driver; + } + + /** + * {@inheritDoc} + */ + protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + } + + /** + * {@inheritDoc} + */ + protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + } + + /** + * {@inheritDoc} + */ + protected function isEntity(ClassMetadataInterface $class) + { + return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument; + } + + /** + * {@inheritDoc} + */ + protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = array()) + { + /** @var $class ClassMetadata */ + /** @var $parent ClassMetadata */ + if ($parent) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorField($parent->discriminatorField); + $class->setDiscriminatorMap($parent->discriminatorMap); + $class->setIdGeneratorType($parent->generatorType); + $this->addInheritedFields($class, $parent); + $this->addInheritedIndexes($class, $parent); + $class->setIdentifier($parent->identifier); + $class->setVersioned($parent->isVersioned); + $class->setVersionField($parent->versionField); + $class->setLifecycleCallbacks($parent->lifecycleCallbacks); + $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); + $class->setFile($parent->getFile()); + if ($parent->isMappedSuperclass) { + $class->setCustomRepositoryClass($parent->customRepositoryClassName); + } + } + + // Invoke driver + try { + $this->driver->loadMetadataForClass($class->getName(), $class); + } catch (\ReflectionException $e) { + throw MappingException::reflectionFailure($class->getName(), $e); + } + + $this->validateIdentifier($class); + + if ($parent && $rootEntityFound) { + if ($parent->generatorType) { + $class->setIdGeneratorType($parent->generatorType); + } + if ($parent->generatorOptions) { + $class->setIdGeneratorOptions($parent->generatorOptions); + } + if ($parent->idGenerator) { + $class->setIdGenerator($parent->idGenerator); + } + } else { + $this->completeIdGeneratorMapping($class); + } + + if ($parent && $parent->isInheritanceTypeSingleCollection()) { + $class->setDatabase($parent->getDatabase()); + $class->setCollection($parent->getCollection()); + } + + $class->setParentClasses($nonSuperclassParents); + + if ($this->evm->hasListeners(Events::loadClassMetadata)) { + $eventArgs = new \Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs($class, $this->dm); + $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); + } + } + + /** + * Validates the identifier mapping. + * + * @param ClassMetadata $class + */ + protected function validateIdentifier($class) + { + if ( ! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument) { + throw MappingException::identifierRequired($class->name); + } + } + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * @return Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + protected function newClassMetadataInstance($className) + { + return new ClassMetadata($className); + } + + private function completeIdGeneratorMapping(ClassMetadataInfo $class) + { + $idGenOptions = $class->generatorOptions; + switch ($class->generatorType) { + case ClassMetadata::GENERATOR_TYPE_AUTO: + $class->setIdGenerator(new \Doctrine\ODM\MongoDB\Id\AutoGenerator($class)); + break; + case ClassMetadata::GENERATOR_TYPE_INCREMENT: + $incrementGenerator = new \Doctrine\ODM\MongoDB\Id\IncrementGenerator($class); + if (isset($idGenOptions['key'])) { + $incrementGenerator->setKey($idGenOptions['key']); + } + if (isset($idGenOptions['collection'])) { + $incrementGenerator->setCollection($idGenOptions['collection']); + } + $class->setIdGenerator($incrementGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_UUID: + $uuidGenerator = new \Doctrine\ODM\MongoDB\Id\UuidGenerator($class); + isset($idGenOptions['salt']) && $uuidGenerator->setSalt($idGenOptions['salt']); + $class->setIdGenerator($uuidGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_ALNUM: + $alnumGenerator = new \Doctrine\ODM\MongoDB\Id\AlnumGenerator($class); + if (isset($idGenOptions['pad'])) { + $alnumGenerator->setPad($idGenOptions['pad']); + } + if (isset($idGenOptions['chars'])) { + $alnumGenerator->setChars($idGenOptions['chars']); + } elseif (isset($idGenOptions['awkwardSafe'])) { + $alnumGenerator->setAwkwardSafeMode($idGenOptions['awkwardSafe']); + } + + $class->setIdGenerator($alnumGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_CUSTOM: + if(empty($idGenOptions['class'])) { + throw MappingException::missingIdGeneratorClass($class->name); + } + + $customGenerator = new $idGenOptions['class']; + unset($idGenOptions['class']); + if(!$customGenerator instanceof \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator) { + throw MappingException::classIsNotAValidGenerator(get_class($customGenerator)); + } + + $methods = get_class_methods($customGenerator); + foreach($idGenOptions as $name => $value) { + $method = 'set' . ucfirst($name); + if(!in_array($method, $methods)) { + throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name); + } + + $customGenerator->$method($value); + } + $class->setIdGenerator($customGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_NONE; + break; + default: + throw new MappingException("Unknown generator type: " . $class->generatorType); + } + } + + /** + * Adds inherited fields to the subclass mapping. + * + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $subClass + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentClass + */ + private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedFieldMapping($mapping); + } + foreach ($parentClass->reflFields as $name => $field) { + $subClass->reflFields[$name] = $field; + } + } + + /** + * Adds inherited indexes to the subclass mapping. + * + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $subClass + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentClass + */ + private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->indexes as $index) { + $subClass->addIndex($index['keys'], $index['options']); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php new file mode 100644 index 00000000..693d253b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php @@ -0,0 +1,1674 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping; + +use InvalidArgumentException; +use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\LockException; +use Doctrine\ODM\MongoDB\Proxy\Proxy; + +/** + * A ClassMetadata instance holds all the object-document mapping metadata + * of a document and it's references. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata +{ + /* The Id generator types. */ + /** + * AUTO means Doctrine will automatically create a new \MongoId instance for us. + */ + const GENERATOR_TYPE_AUTO = 1; + + /** + * INCREMENT means a separate collection is used for maintaining and incrementing id generation. + * Offers full portability. + */ + const GENERATOR_TYPE_INCREMENT = 2; + + /** + * UUID means Doctrine will generate a uuid for us. + */ + const GENERATOR_TYPE_UUID = 3; + + /** + * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT + * generator to ensure identifier uniqueness + */ + const GENERATOR_TYPE_ALNUM = 4; + + /** + * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class + * and pass other options to the generator. It will throw an Exception if the class + * does not exist or if an option was passed for that there is not setter in the new + * generator class. + * + * The class will have to be a subtype of AbstractIdGenerator. + */ + const GENERATOR_TYPE_CUSTOM = 5; + + /** + * NONE means Doctrine will not generate any id for us and you are responsible for manually + * assigning an id. + */ + const GENERATOR_TYPE_NONE = 6; + + + const REFERENCE_ONE = 1; + const REFERENCE_MANY = 2; + const EMBED_ONE = 3; + const EMBED_MANY = 4; + const MANY = 'many'; + const ONE = 'one'; + + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + const INHERITANCE_TYPE_NONE = 1; + + /** + * SINGLE_COLLECTION means the class will be persisted according to the rules of + * Single Collection Inheritance. + */ + const INHERITANCE_TYPE_SINGLE_COLLECTION = 2; + + /** + * COLLECTION_PER_CLASS means the class will be persisted according to the rules + * of Concrete Collection Inheritance. + */ + const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3; + + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through persist() or a cascade). + */ + const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + + /** + * NOTIFY means that Doctrine relies on the entities sending out notifications + * when their properties change. Such entity classes must implement + * the NotifyPropertyChanged interface. + */ + const CHANGETRACKING_NOTIFY = 3; + + /** + * READ-ONLY: The name of the mongo database the document is mapped to. + */ + public $db; + + /** + * READ-ONLY: The name of the mongo collection the document is mapped to. + */ + public $collection; + + /** + * READ-ONLY: If the collection should be a fixed size. + */ + public $collectionCapped; + + /** + * READ-ONLY: If the collection is fixed size, its size in bytes. + */ + public $collectionSize; + + /** + * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection. + */ + public $collectionMax; + + /** + * READ-ONLY: The field name of the document identifier. + */ + public $identifier; + + /** + * READ-ONLY: The field that stores a file reference and indicates the + * document is a file and should be stored on the MongoGridFS. + */ + public $file; + + /** + * READ-ONLY: The field that stores the calculated distance when performing geo spatial + * queries. + */ + public $distance; + + /** + * READ-ONLY: Whether or not reads for this class are okay to read from a slave. + */ + public $slaveOkay = false; + + /** + * READ-ONLY: The array of indexes for the document collection. + */ + public $indexes = array(); + + /** + * READ-ONLY: Whether or not queries on this document should require indexes. + */ + public $requireIndexes = false; + + /** + * READ-ONLY: The name of the document class. + */ + public $name; + + /** + * READ-ONLY: The namespace the document class is contained in. + * + * @var string + * @todo Not really needed. Usage could be localized. + */ + public $namespace; + + /** + * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance + * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same + * as {@link $documentName}. + * + * @var string + */ + public $rootDocumentName; + + /** + * The name of the custom repository class used for the document class. + * (Optional). + * + * @var string + */ + public $customRepositoryClassName; + + /** + * READ-ONLY: The names of the parent classes (ancestors). + * + * @var array + */ + public $parentClasses = array(); + + /** + * READ-ONLY: The names of all subclasses (descendants). + * + * @var array + */ + public $subClasses = array(); + + /** + * The ReflectionProperty instances of the mapped class. + * + * @var array + */ + public $reflFields = array(); + + /** + * READ-ONLY: The inheritance mapping type used by the class. + * + * @var integer + */ + public $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * READ-ONLY: The Id generator type used by the class. + * + * @var string + */ + public $generatorType = self::GENERATOR_TYPE_AUTO; + + /** + * READ-ONLY: The Id generator options. + * + * @var array + */ + public $generatorOptions = array(); + + /** + * READ-ONLY: The ID generator used for generating IDs for this class. + * + * @var AbstractIdGenerator + */ + public $idGenerator; + + /** + * READ-ONLY: The field mappings of the class. + * Keys are field names and values are mapping definitions. + * + * The mapping definition array has the following values: + * + * - fieldName (string) + * The name of the field in the Document. + * + * - id (boolean, optional) + * Marks the field as the primary key of the document. Multiple fields of an + * document can have the id attribute, forming a composite key. + * + * @var array + */ + public $fieldMappings = array(); + + /** + * READ-ONLY: The association mappings of the class. + * Keys are field names and values are mapping definitions. + * + * @var array + */ + public $associationMappings = array(); + + /** + * READ-ONLY: Array of fields to also load with a given method. + * + * @var array + */ + public $alsoLoadMethods = array(); + + /** + * READ-ONLY: The registered lifecycle callbacks for documents of this class. + * + * @var array + */ + public $lifecycleCallbacks = array(); + + /** + * READ-ONLY: The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies + * where a discriminator field is used. + * + * @var mixed + * @see discriminatorField + */ + public $discriminatorValue; + + /** + * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the SINGLE_COLLECTION inheritance mapping strategy + * where a discriminator field is used. + * + * @var mixed + * @see discriminatorField + */ + public $discriminatorMap = array(); + + /** + * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION + * inheritance mapping. + * + * @var string + */ + public $discriminatorField; + + /** + * READ-ONLY: Whether this class describes the mapping of a mapped superclass. + * + * @var boolean + */ + public $isMappedSuperclass = false; + + /** + * READ-ONLY: Whether this class describes the mapping of a embedded document. + * + * @var boolean + */ + public $isEmbeddedDocument = false; + + /** + * READ-ONLY: The policy used for change-tracking on entities of this class. + * + * @var integer + */ + public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to be versioned + * with optimistic locking. + * + * @var boolean $isVersioned + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed $versionField + */ + public $versionField; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic + * locking. + * + * @var boolean $isLockable + */ + public $isLockable; + + /** + * READ-ONLY: The name of the field which is used for locking a document. + * + * @var mixed $lockField + */ + public $lockField; + + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass + */ + public $reflClass; + + /** + * Initializes a new ClassMetadata instance that will hold the object-document mapping + * metadata of the class with the given name. + * + * @param string $documentName The name of the document class the new instance is used for. + */ + public function __construct($documentName) + { + $this->name = $documentName; + $this->rootDocumentName = $documentName; + } + + /** + * Gets the ReflectionClass instance of the mapped class. + * + * @return ReflectionClass + */ + public function getReflectionClass() + { + if ( ! $this->reflClass) { + $this->reflClass = new \ReflectionClass($this->name); + } + return $this->reflClass; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * FALSE otherwise. + */ + public function isIdentifier($fieldName) + { + return $this->identifier === $fieldName ? true : false; + } + + /** + * INTERNAL: + * Sets the mapped identifier field of this class. + * + * @param string $identifier + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + } + + /** + * Gets the mapped identifier field of this class. + * + * @return string $identifier + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Get identifier field names of this class. + * + * Since MongoDB only allows exactly one identifier field this is a proxy + * to {@see getIdentifier()} and returns an array. + * + * @return array + */ + public function getIdentifierFieldNames() + { + return array($this->identifier); + } + + /** + * Checks whether the class has a (mapped) field with a certain name. + * + * @return boolean + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Sets the inheritance type used by the class and it's subclasses. + * + * @param integer $type + */ + public function setInheritanceType($type) + { + $this->inheritanceType = $type; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + * + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Registers a custom repository class for the document class. + * + * @param string $mapperClassName The class name of the custom mapper. + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->customRepositoryClassName = $repositoryClassName; + } + + /** + * Dispatches the lifecycle event of the given document to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @param string $event The lifecycle event. + * @param Document $document The Document on which the event occurred. + */ + public function invokeLifecycleCallbacks($lifecycleEvent, $document, array $arguments = null) + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + if ($arguments !== null) { + call_user_func_array(array($document, $callback), $arguments); + } else { + $document->$callback(); + } + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + * + * @param string $lifecycleEvent + * @return boolean + */ + public function hasLifecycleCallbacks($lifecycleEvent) + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @param string $event + * @return array + */ + public function getLifecycleCallbacks($event) + { + return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); + } + + /** + * Adds a lifecycle callback for documents of this class. + * + * Note: If the same callback is registered more than once, the old one + * will be overridden. + * + * @param string $callback + * @param string $event + */ + public function addLifecycleCallback($callback, $event) + { + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for documents of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Sets the discriminator field name. + * + * @param string $discriminatorField + * @see getDiscriminatorField() + */ + public function setDiscriminatorField($discriminatorField) + { + if ( ! isset($discriminatorField['name']) && isset($discriminatorField['fieldName'])) { + $discriminatorField['name'] = $discriminatorField['fieldName']; + } + if (isset($this->fieldMappings[$discriminatorField['name']])) { + throw MappingException::duplicateFieldMapping($this->name, $discriminatorField['name']); + } + $this->discriminatorField = $discriminatorField; + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + if (strpos($className, '\\') === false && strlen($this->namespace)) { + $className = $this->namespace . '\\' . $className; + } + $this->discriminatorMap[$value] = $className; + if ($this->name == $className) { + $this->discriminatorValue = $value; + } else { + if ( ! class_exists($className)) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + if (is_subclass_of($className, $this->name)) { + $this->subClasses[] = $className; + } + } + } + } + + /** + * Sets the discriminator value for this class. + * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single + * collection. + * + * @param string $value + */ + public function setDiscriminatorValue($value) + { + $this->discriminatorMap[$value] = $this->name; + $this->discriminatorValue = $value; + } + + /** + * Sets whether or not reads for this class are okay to read from a slave. + * + * @param bool $slaveOkay + */ + public function setSlaveOkay($slaveOkay) + { + $this->slaveOkay = $slaveOkay; + } + + /** + * Add a index for this Document. + * + * @param array $keys Array of keys for the index. + * @param array $options Array of options for the index. + */ + public function addIndex($keys, array $options = array()) + { + $this->indexes[] = array( + 'keys' => array_map(function($value) { + if ($value == 1 || $value == -1) { + return (int) $value; + } elseif(is_string($value)) { + $lower = strtolower($value); + if ($lower === 'asc') { + return 1; + } elseif ($lower === 'desc') { + return -1; + } else { + return $value; + } + } else { + return $value; + } + }, $keys), + 'options' => $options + ); + } + + /** + * Set whether or not queries on this document should require indexes. + * + * @param bool $requireIndexes + */ + public function setRequireIndexes($requireIndexes) + { + $this->requireIndexes = $requireIndexes; + } + + /** + * Returns the array of indexes for this Document. + * + * @return array $indexes The array of indexes. + */ + public function getIndexes() + { + return $this->indexes; + } + + /** + * Checks whether this document has indexes or not. + * + * @return boolean + */ + public function hasIndexes() + { + return $this->indexes ? true : false; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + */ + public function setChangeTrackingPolicy($policy) + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredExplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredImplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Whether the change tracking policy of this class is "notify". + * + * @return boolean + */ + public function isChangeTrackingNotify() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; + } + + /** + * Gets the ReflectionProperties of the mapped class. + * + * @return array An array of ReflectionProperty instances. + */ + public function getReflectionProperties() + { + return $this->reflFields; + } + + /** + * Gets a ReflectionProperty for a specific field of the mapped class. + * + * @param string $name + * @return ReflectionProperty + */ + public function getReflectionProperty($name) + { + return $this->reflFields[$name]; + } + + /** + * The name of this Document class. + * + * @return string $name The Document class name. + */ + public function getName() + { + return $this->name; + } + + /** + * The namespace this Document class belongs to. + * + * @return string $namespace The namespace name. + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Returns the database this Document is mapped to. + * + * @return string $db The database name. + */ + public function getDatabase() + { + return $this->db; + } + + /** + * Set the database this Document is mapped to. + * + * @param string $db The database name + */ + public function setDatabase($db) + { + $this->db = $db; + } + + /** + * Get the collection this Document is mapped to. + * + * @return string $collection The collection name. + */ + public function getCollection() + { + return $this->collection; + } + + /** + * Sets the collection this Document is mapped to. + * + * @param string $collection The collection name. + */ + public function setCollection($name) + { + if (is_array($name)) { + if ( ! isset($name['name'])) { + throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()'); + } + $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false; + $this->collectionSize = isset($name['size']) ? $name['size'] : 0; + $this->collectionMax = isset($name['max']) ? $name['max'] : 0; + $this->collection = $name['name']; + } else { + $this->collection = $name; + } + } + + /** + * Get whether or not the documents collection is capped. + * + * @return boolean + */ + public function getCollectionCapped() + { + return $this->collectionCapped; + } + + /** + * Set whether or not the documents collection is capped. + * + * @param boolean $bool + */ + public function setCollectionCapped($bool) + { + $this->collectionCapped = $bool; + } + + /** + * Get the collection size + * + * @return integer + */ + public function getCollectionSize() + { + return $this->collectionSize; + } + + /** + * Set the collection size. + * + * @param integer $size + */ + public function setCollectionSize($size) + { + $this->collectionSize = $size; + } + + /** + * Get the collection max. + * + * @return integer + */ + public function getCollectionMax() + { + return $this->collectionMax; + } + + /** + * Set the collection max. + * + * @param integer $max + */ + public function setCollectionMax($max) + { + $this->collectionMax = $max; + } + + /** + * Returns TRUE if this Document is mapped to a collection FALSE otherwise. + * + * @return boolean + */ + public function isMappedToCollection() + { + return $this->collection ? true : false; + } + + /** + * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise. + * + * @return boolean + */ + public function isFile() + { + return $this->file ? true :false; + } + + /** + * Returns the file field name. + * + * @return string $file The file field name. + */ + public function getFile() + { + return $this->file; + } + + /** + * Set the field name that stores the grid file. + * + * @param string $file + */ + public function setFile($file) + { + $this->file = $file; + } + + /** + * Returns the distance field name. + * + * @return string $distance The distance field name. + */ + public function getDistance() + { + return $this->distance; + } + + /** + * Set the field name that stores the distance. + * + * @param string $distance + */ + public function setDistance($distance) + { + $this->distance = $distance; + } + + /** + * Map a field. + * + * @param array $mapping The mapping information. + */ + public function mapField(array $mapping) + { + if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) { + $mapping['fieldName'] = $mapping['name']; + } + if ( ! isset($mapping['fieldName'])) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['name'])) { + $mapping['name'] = $mapping['fieldName']; + } + if (isset($this->fieldMappings[$mapping['fieldName']])) { + //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']); + } + if ($this->discriminatorField['name'] === $mapping['fieldName']) { + throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']); + } + if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) { + $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument']; + } + + if (isset($mapping['discriminatorMap'])) { + foreach ($mapping['discriminatorMap'] as $key => $class) { + if (strpos($class, '\\') === false && strlen($this->namespace)) { + $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class; + } + } + } + + if (isset($mapping['cascade']) && is_string($mapping['cascade'])) { + $mapping['cascade'] = array($mapping['cascade']); + } + if (isset($mapping['cascade']) && in_array('all', (array) $mapping['cascade'])) { + unset($mapping['cascade']); + $default = true; + } else { + $default = false; + } + $mapping['isCascadeRemove'] = $default; + $mapping['isCascadePersist'] = $default; + $mapping['isCascadeRefresh'] = $default; + $mapping['isCascadeMerge'] = $default; + $mapping['isCascadeDetach'] = $default; + $mapping['isCascadeCallbacks'] = $default; + if (isset($mapping['cascade']) && is_array($mapping['cascade'])) { + foreach ($mapping['cascade'] as $cascade) { + $mapping['isCascade' . ucfirst($cascade)] = true; + } + } + unset($mapping['cascade']); + if (isset($mapping['type']) && $mapping['type'] === 'file') { + $mapping['file'] = true; + } + if (isset($mapping['file']) && $mapping['file'] === true) { + $this->file = $mapping['fieldName']; + $mapping['name'] = 'file'; + } + if (isset($mapping['distance']) && $mapping['distance'] === true) { + $this->distance = $mapping['fieldName']; + } + if (isset($mapping['id']) && $mapping['id'] === true) { + $mapping['name'] = '_id'; + $mapping['type'] = isset($mapping['type']) ? $mapping['type'] : 'id'; + $this->identifier = $mapping['fieldName']; + if (isset($mapping['strategy'])) { + $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array(); + + $generatorType = constant('Doctrine\ODM\MongoDB\Mapping\ClassMetadata::GENERATOR_TYPE_' . strtoupper($mapping['strategy'])); + if ($generatorType !== self::GENERATOR_TYPE_AUTO) { + $mapping['type'] = isset($this->generatorOptions['type']) + ? $this->generatorOptions['type'] + : 'custom_id'; + + unset($this->generatorOptions['type']); + } + + $this->generatorType = $generatorType; + } + } + if ( ! isset($mapping['nullable'])) { + $mapping['nullable'] = false; + } + if (isset($mapping['reference']) && $mapping['type'] === 'one') { + $mapping['association'] = self::REFERENCE_ONE; + } + if (isset($mapping['reference']) && $mapping['type'] === 'many') { + $mapping['association'] = self::REFERENCE_MANY; + } + if (isset($mapping['embedded']) && $mapping['type'] === 'one') { + $mapping['association'] = self::EMBED_ONE; + } + if (isset($mapping['embedded']) && $mapping['type'] === 'many') { + $mapping['association'] = self::EMBED_MANY; + } + + /* + if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) { + $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY; + } + */ + if (isset($mapping['version'])) { + $mapping['notSaved'] = true; + $this->setVersionMapping($mapping); + } + if (isset($mapping['lock'])) { + $mapping['notSaved'] = true; + $this->setLockMapping($mapping); + } + $mapping['isOwningSide'] = true; + $mapping['isInverseSide'] = false; + if (isset($mapping['reference'])) { + if (isset($mapping['inversedBy']) && $mapping['inversedBy']) { + $mapping['isOwningSide'] = true; + $mapping['isInverseSide'] = false; + } + if (isset($mapping['mappedBy']) && $mapping['mappedBy']) { + $mapping['isInverseSide'] = true; + $mapping['isOwningSide'] = false; + } + } + + $this->fieldMappings[$mapping['fieldName']] = $mapping; + if (isset($mapping['association'])) { + $this->associationMappings[$mapping['fieldName']] = $mapping; + } + + return $mapping; + } + + /** + * Map a MongoGridFSFile. + * + * @param array $mapping The mapping information. + */ + public function mapFile(array $mapping) + { + $mapping['file'] = true; + $mapping['type'] = 'file'; + $this->mapField($mapping); + } + + /** + * Map a single embedded document. + * + * @param array $mapping The mapping information. + */ + public function mapOneEmbedded(array $mapping) + { + $mapping['embedded'] = true; + $mapping['type'] = 'one'; + $this->mapField($mapping); + } + + /** + * Map a collection of embedded documents. + * + * @param array $mapping The mapping information. + */ + public function mapManyEmbedded(array $mapping) + { + $mapping['embedded'] = true; + $mapping['type'] = 'many'; + $this->mapField($mapping); + } + + /** + * Map a single document reference. + * + * @param array $mapping The mapping information. + */ + public function mapOneReference(array $mapping) + { + $mapping['reference'] = true; + $mapping['type'] = 'one'; + $this->mapField($mapping); + } + + /** + * Map a collection of document references. + * + * @param array $mapping The mapping information. + */ + public function mapManyReference(array $mapping) + { + $mapping['reference'] = true; + $mapping['type'] = 'many'; + $this->mapField($mapping); + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + * + * @param array $mapping + */ + public function addInheritedFieldMapping(array $fieldMapping) + { + $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; + } + + /** + * Checks whether the class has a mapped association with the given field name. + * + * @param string $fieldName + * @return boolean + */ + public function hasReference($fieldName) + { + return isset($this->fieldMappings[$fieldName]['reference']); + } + + /** + * Checks whether the class has a mapped embed with the given field name. + * + * @param string $fieldName + * @return boolean + */ + public function hasEmbed($fieldName) + { + return isset($this->fieldMappings[$fieldName]['embedded']); + } + + /** + * Checks whether the class has a mapped association (embed or reference) with the given field name. + * + * @param string $fieldName + * @return boolean + */ + public function hasAssociation($fieldName) + { + return $this->hasReference($fieldName) || $this->hasEmbed($fieldName); + } + + /** + * Checks whether the class has a mapped reference or embed for the specified field and + * is a single valued association. + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. + */ + public function isSingleValuedAssociation($fieldName) + { + return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName); + } + + /** + * Checks whether the class has a mapped reference or embed for the specified field and + * is a collection valued association. + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. + */ + public function isCollectionValuedAssociation($fieldName) + { + return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName); + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a single-valued association (to-one). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. + */ + public function isSingleValuedReference($fieldName) + { + return isset($this->fieldMappings[$fieldName]['association']) && + $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE; + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a collection-valued association (to-many). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. + */ + public function isCollectionValuedReference($fieldName) + { + return isset($this->fieldMappings[$fieldName]['association']) && + $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY; + } + + /** + * Checks whether the class has a mapped embedded document for the specified field + * and if yes, checks whether it is a single-valued association (to-one). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. + */ + public function isSingleValuedEmbed($fieldName) + { + return isset($this->fieldMappings[$fieldName]['association']) && + $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE; + } + + /** + * Checks whether the class has a mapped embedded document for the specified field + * and if yes, checks whether it is a collection-valued association (to-many). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. + */ + public function isCollectionValuedEmbed($fieldName) + { + return isset($this->fieldMappings[$fieldName]['association']) && + $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY; + } + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param AbstractIdGenerator $generator + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Casts the identifier to its portable PHP type. + * + * @param mixed $id + * @return mixed $id + */ + public function getPHPIdentifierValue($id) + { + $idType = $this->fieldMappings[$this->identifier]['type']; + return Types\Type::getType($idType)->convertToPHPValue($id); + } + + /** + * Casts the identifier to its database type. + * + * @param mixed $id + * @return mixed $id + */ + public function getDatabaseIdentifierValue($id) + { + $idType = $this->fieldMappings[$this->identifier]['type']; + return Types\Type::getType($idType)->convertToDatabaseValue($id); + } + + /** + * Sets the document identifier of a document. + * + * @param object $document + * @param mixed $id + */ + public function setIdentifierValue($document, $id) + { + $id = $this->getPHPIdentifierValue($id); + $this->reflFields[$this->identifier]->setValue($document, $id); + } + + /** + * Gets the document identifier. + * + * @param object $document + * @return string $id + */ + public function getIdentifierValue($document) + { + if ($document instanceof Proxy && !$document->__isInitialized()) { + return $document->__identifier__; + } + return $this->reflFields[$this->identifier]->getValue($document); + } + + /** + * Get identifier values of this document. + * + * Since MongoDB only allows exactly one identifier field this is a proxy + * to {@see getIdentifierValue()} and returns an array with the identifier + * field as a key. + * + * @param object $document + * @return array + */ + public function getIdentifierValues($document) + { + return array($this->identifier => $this->getIdentifierValue($document)); + } + + /** + * Get the document identifier object. + * + * @param string $document + * @return MongoId $id The MongoID object. + */ + public function getIdentifierObject($document) + { + if ($id = $this->getIdentifierValue($document)) { + return $this->getDatabaseIdentifierValue($id); + } + } + + /** + * Sets the specified field to the specified value on the given document. + * + * @param object $document + * @param string $field + * @param mixed $value + */ + public function setFieldValue($document, $field, $value) + { + $this->reflFields[$field]->setValue($document, $value); + } + + /** + * Gets the specified field's value off the given document. + * + * @param object $document + * @param string $field + */ + public function getFieldValue($document, $field) + { + if ($document instanceof Proxy && $field === $this->identifier && !$document->__isInitialized()) { + return $document->__identifier__; + } + return $this->reflFields[$field]->getValue($document); + } + + /** + * Gets the mapping of a field. + * + * @param string $fieldName The field name. + * @return array The field mapping. + */ + public function getFieldMapping($fieldName) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->fieldMappings[$fieldName]; + } + + /** + * Check if the field is not null. + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is not null, FALSE otherwise. + */ + public function isNullable($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['nullable']) && $mapping['nullable'] == true; + } + return false; + } + + /** + * Checks whether the document has a discriminator field and value configured. + * + * @return boolean + */ + public function hasDiscriminator() + { + return isset($this->discriminatorField) && isset($this->discriminatorValue) ? true : false; + } + + /** + * Sets the type of Id generator to use for the mapped class. + */ + public function setIdGeneratorType($generatorType) + { + $this->generatorType = $generatorType; + } + + /** + * Sets the Id generator options. + */ + public function setIdGeneratorOptions($generatorOptions) + { + $this->generatorOptions = $generatorOptions; + } + + /** + * @return boolean + */ + public function isInheritanceTypeNone() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a SINGLE_COLLECTION inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleCollection() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION; + } + + /** + * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a COLLECTION_PER_CLASS inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeCollectionPerClass() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS; + } + + /** + * Sets the mapped subclasses of this class. + * + * @param array $subclasses The names of all mapped subclasses. + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + if (strpos($subclass, '\\') === false && strlen($this->namespace)) { + $this->subClasses[] = $this->namespace . '\\' . $subclass; + } else { + $this->subClasses[] = $subclass; + } + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + */ + public function setParentClasses(array $classNames) + { + $this->parentClasses = $classNames; + if (count($classNames) > 0) { + $this->rootDocumentName = array_pop($classNames); + } + } + + /** + * Checks whether the class will generate a new \MongoId instance for us. + * + * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise. + */ + public function isIdGeneratorAuto() + { + return $this->generatorType == self::GENERATOR_TYPE_AUTO; + } + + /** + * Checks whether the class will use a collection to generate incremented identifiers. + * + * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise. + */ + public function isIdGeneratorIncrement() + { + return $this->generatorType == self::GENERATOR_TYPE_INCREMENT; + } + + /** + * Checks whether the class will generate a uuid id. + * + * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise. + */ + public function isIdGeneratorUuid() + { + return $this->generatorType == self::GENERATOR_TYPE_UUID; + } + + /** + * Checks whether the class uses no id generator. + * + * @return boolean TRUE if the class does not use any id generator, FALSE otherwise. + */ + public function isIdGeneratorNone() + { + return $this->generatorType == self::GENERATOR_TYPE_NONE; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @param array $mapping The version field mapping array + */ + public function setVersionMapping(array &$mapping) + { + if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') { + throw LockException::invalidVersionFieldType($mapping['type']); + } + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + * + * @param boolean $bool + */ + public function setVersioned($bool) + { + $this->isVersioned = $bool; + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + * + * @param string $versionField + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @param array $mapping The version field mapping array + */ + public function setLockMapping(array &$mapping) + { + if ($mapping['type'] !== 'int') { + throw LockException::invalidLockFieldType($mapping['type']); + } + $this->isLockable = true; + $this->lockField = $mapping['fieldName']; + } + + /** + * Sets whether this class is to allow pessimistic locking. + * + * @param boolean $bool + */ + public function setLockable($bool) + { + $this->isLockable = $bool; + } + + /** + * Sets the name of the field that is to be used for storing whether a document + * is currently locked or not. + * + * @param string $lockField + */ + public function setLockField($lockField) + { + $this->lockField = $lockField; + } + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + public function getFieldNames() + { + return array_keys($this->fieldMappings); + } + + /** + * A numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + public function getAssociationNames() + { + return array_keys($this->associationMappings); + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * @return Doctrine\DBAL\Types\Type + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * @return string + */ + public function getAssociationTargetClass($assocName) + { + if ( ! isset($this->associationMappings[$assocName])) { + throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); + } + + return $this->associationMappings[$assocName]['targetDocument']; + } + + /** + * @param string $fieldName + * @return bool + */ + public function isAssociationInverseSide($fieldName) + { + throw new \BadMethodCallException(__METHOD__.'() is not implemented yet.'); + } + + /** + * @param string $fieldName + * @return string + */ + public function getAssociationMappedByTargetField($fieldName) + { + throw new \BadMethodCallException(__METHOD__.'() is not implemented yet.'); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 00000000..6edde0c1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,232 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\Reader; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\ODM\MongoDB\Events; +use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; +use Doctrine\ODM\MongoDB\Mapping\MappingException; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class AnnotationDriver extends AbstractAnnotationDriver +{ + protected $entityAnnotationClasses = array( + 'Doctrine\\ODM\\MongoDB\\Mapping\\Annotations\\Document' => 1, + 'Doctrine\\ODM\\MongoDB\\Mapping\\Annotations\\MappedSuperclass' => 2, + 'Doctrine\\ODM\\MongoDB\\Mapping\\Annotations\\EmbeddedDocument' => 3, + ); + + /** + * Registers annotation classes to the common registry. + * + * This method should be called when bootstrapping your application. + */ + public static function registerAnnotationClasses() + { + AnnotationRegistry::registerFile(__DIR__ . '/../Annotations/DoctrineAnnotations.php'); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $class) + { + /** @var $class ClassMetadataInfo */ + $reflClass = $class->getReflectionClass(); + + $documentAnnots = array(); + foreach ($this->reader->getClassAnnotations($reflClass) as $annot) { + foreach ($this->entityAnnotationClasses as $annotClass => $i) { + if ($annot instanceof $annotClass) { + $documentAnnots[$i] = $annot; + continue 2; + } + } + + // non-document class annotations + if ($annot instanceof ODM\AbstractIndex) { + $this->addIndex($class, $annot); + } + if ($annot instanceof ODM\Indexes) { + foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) { + $this->addIndex($class, $index); + } + } elseif ($annot instanceof ODM\InheritanceType) { + $class->setInheritanceType(constant('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata::INHERITANCE_TYPE_'.$annot->value)); + } elseif ($annot instanceof ODM\DiscriminatorField) { + $class->setDiscriminatorField(array('fieldName' => $annot->fieldName, 'name' => $annot->name)); + } elseif ($annot instanceof ODM\DiscriminatorMap) { + $class->setDiscriminatorMap($annot->value); + } elseif ($annot instanceof ODM\DiscriminatorValue) { + $class->setDiscriminatorValue($annot->value); + } elseif ($annot instanceof ODM\ChangeTrackingPolicy) { + $class->setChangeTrackingPolicy(constant('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata::CHANGETRACKING_'.$annot->value)); + } + + } + + if (!$documentAnnots) { + throw MappingException::classIsNotAValidDocument($className); + } + + // find the winning document annotation + ksort($documentAnnots); + $documentAnnot = reset($documentAnnots); + + if ($documentAnnot instanceof ODM\MappedSuperclass) { + $class->isMappedSuperclass = true; + } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) { + $class->isEmbeddedDocument = true; + } + if (isset($documentAnnot->db)) { + $class->setDatabase($documentAnnot->db); + } + if (isset($documentAnnot->collection)) { + $class->setCollection($documentAnnot->collection); + } + if (isset($documentAnnot->repositoryClass)) { + $class->setCustomRepositoryClass($documentAnnot->repositoryClass); + } + if (isset($documentAnnot->indexes)) { + foreach ($documentAnnot->indexes as $index) { + $this->addIndex($class, $index); + } + } + if (isset($documentAnnot->requireIndexes)) { + $class->setRequireIndexes($documentAnnot->requireIndexes); + } + if (isset($documentAnnot->slaveOkay)) { + $class->setSlaveOkay($documentAnnot->slaveOkay); + } + + foreach ($reflClass->getProperties() as $property) { + if ($class->isMappedSuperclass && !$property->isPrivate()) { + continue; + } + + $indexes = array(); + $mapping = array('fieldName' => $property->getName()); + $fieldAnnot = null; + + foreach ($this->reader->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof ODM\AbstractField) { + $fieldAnnot = $annot; + } + if ($annot instanceof ODM\AbstractIndex) { + $indexes[] = $annot; + } + if ($annot instanceof ODM\Indexes) { + foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) { + $indexes[] = $index; + } + } elseif ($annot instanceof ODM\AlsoLoad) { + $mapping['alsoLoadFields'] = (array) $annot->value; + } elseif ($annot instanceof ODM\Version) { + $mapping['version'] = true; + } elseif ($annot instanceof ODM\Lock) { + $mapping['lock'] = true; + } + } + + if ($fieldAnnot) { + $mapping = array_replace($mapping, (array) $fieldAnnot); + $class->mapField($mapping); + } + + if ($indexes) { + foreach ($indexes as $index) { + $name = isset($mapping['name']) ? $mapping['name'] : $mapping['fieldName']; + $keys = array($name => $index->order ?: 'asc'); + $this->addIndex($class, $index, $keys); + } + } + } + + foreach ($reflClass->getMethods() as $method) { + if ($method->isPublic()) { + foreach ($this->reader->getMethodAnnotations($method) as $annot) { + if ($annot instanceof ODM\AlsoLoad) { + foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $field) { + $class->alsoLoadMethods[$field] = $method->getName(); + } + } elseif ($annot instanceof ODM\PrePersist) { + $class->addLifecycleCallback($method->getName(), Events::prePersist); + } elseif ($annot instanceof ODM\PostPersist) { + $class->addLifecycleCallback($method->getName(), Events::postPersist); + } elseif ($annot instanceof ODM\PreUpdate) { + $class->addLifecycleCallback($method->getName(), Events::preUpdate); + } elseif ($annot instanceof ODM\PostUpdate) { + $class->addLifecycleCallback($method->getName(), Events::postUpdate); + } elseif ($annot instanceof ODM\PreRemove) { + $class->addLifecycleCallback($method->getName(), Events::preRemove); + } elseif ($annot instanceof ODM\PostRemove) { + $class->addLifecycleCallback($method->getName(), Events::postRemove); + } elseif ($annot instanceof ODM\PreLoad) { + $class->addLifecycleCallback($method->getName(), Events::preLoad); + } elseif ($annot instanceof ODM\PostLoad) { + $class->addLifecycleCallback($method->getName(), Events::postLoad); + } elseif ($annot instanceof ODM\PreFlush) { + $class->addLifecycleCallback($method->getName(), Events::preFlush); + } + } + } + } + } + + private function addIndex(ClassMetadataInfo $class, $index, array $keys = array()) + { + $keys = array_merge($keys, $index->keys); + $options = array(); + $allowed = array('name', 'dropDups', 'background', 'safe', 'unique', 'sparse', 'expireAfterSeconds'); + foreach ($allowed as $name) { + if (isset($index->$name)) { + $options[$name] = $index->$name; + } + } + $options = array_merge($options, $index->options); + $class->addIndex($keys, $options); + } + + /** + * Factory method for the Annotation Driver + * + * @param array|string $paths + * @param Reader $reader + * @return AnnotationDriver + */ + static public function create($paths = array(), Reader $reader = null) + { + if ($reader == null) { + $reader = new AnnotationReader(); + } + return new self($reader, $paths); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php new file mode 100644 index 00000000..be41ba04 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php @@ -0,0 +1,335 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class XmlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $class) + { + /* @var $class ClassMetadataInfo */ + /* @var $xmlRoot SimpleXMLElement */ + $xmlRoot = $this->getElement($className); + if ( ! $xmlRoot) { + return; + } + + if ($xmlRoot->getName() == 'document') { + if (isset($xmlRoot['repository-class'])) { + $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']); + } + } elseif ($xmlRoot->getName() == 'mapped-superclass') { + $class->isMappedSuperclass = true; + } elseif ($xmlRoot->getName() == 'embedded-document') { + $class->isEmbeddedDocument = true; + } + if (isset($xmlRoot['db'])) { + $class->setDatabase((string) $xmlRoot['db']); + } + if (isset($xmlRoot['collection'])) { + $class->setCollection((string) $xmlRoot['collection']); + } + if (isset($xmlRoot['inheritance-type'])) { + $inheritanceType = (string) $xmlRoot['inheritance-type']; + $class->setInheritanceType(constant('Doctrine\ODM\MongoDB\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType)); + } + if (isset($xmlRoot['change-tracking-policy'])) { + $class->setChangeTrackingPolicy(constant('Doctrine\ODM\MongoDB\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper((string)$xmlRoot['change-tracking-policy']))); + } + if (isset($xmlRoot->{'discriminator-field'})) { + $discrField = $xmlRoot->{'discriminator-field'}; + $class->setDiscriminatorField(array( + 'name' => isset($discrField['name']) ? (string) $discrField['name'] : null, + 'fieldName' => (string) $discrField['fieldName'], + )); + } + if (isset($xmlRoot->{'discriminator-map'})) { + $map = array(); + foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} AS $discrMapElement) { + $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class']; + } + $class->setDiscriminatorMap($map); + } + if (isset($xmlRoot->{'indexes'})) { + foreach($xmlRoot->{'indexes'}->{'index'} as $index) { + $this->addIndex($class, $index); + } + } + if (isset($xmlRoot->{'require-indexes'})) { + $class->setRequireIndexes((bool) $xmlRoot->{'require-indexes'}); + } + if (isset($xmlRoot->{'slave-okay'})) { + $class->setSlaveOkay((bool) $xmlRoot->{'slave-okay'}); + } + if (isset($xmlRoot->field)) { + foreach ($xmlRoot->field as $field) { + $mapping = array(); + $attributes = $field->attributes(); + foreach ($attributes as $key => $value) { + $mapping[$key] = (string) $value; + $booleanAttributes = array('id', 'reference', 'embed', 'unique', 'sparse', 'file', 'distance'); + if (in_array($key, $booleanAttributes)) { + $mapping[$key] = ('true' === $mapping[$key]) ? true : false; + } + } + $this->addFieldMapping($class, $mapping); + } + } + if (isset($xmlRoot->{'embed-one'})) { + foreach ($xmlRoot->{'embed-one'} as $embed) { + $this->addEmbedMapping($class, $embed, 'one'); + } + } + if (isset($xmlRoot->{'embed-many'})) { + foreach ($xmlRoot->{'embed-many'} as $embed) { + $this->addEmbedMapping($class, $embed, 'many'); + } + } + if (isset($xmlRoot->{'reference-many'})) { + foreach ($xmlRoot->{'reference-many'} as $reference) { + $this->addReferenceMapping($class, $reference, 'many'); + } + } + if (isset($xmlRoot->{'reference-one'})) { + foreach ($xmlRoot->{'reference-one'} as $reference) { + $this->addReferenceMapping($class, $reference, 'one'); + } + } + if (isset($xmlRoot->{'lifecycle-callbacks'})) { + foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { + $class->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ODM\MongoDB\Events::' . (string) $lifecycleCallback['type'])); + } + } + } + + private function addFieldMapping(ClassMetadataInfo $class, $mapping) + { + $keys = null; + + if (isset($mapping['name'])) { + $name = $mapping['name']; + } elseif (isset($mapping['fieldName'])) { + $name = $mapping['fieldName']; + } else { + throw new \InvalidArgumentException('Cannot infer a MongoDB name from the mapping'); + } + + if (isset($mapping['type']) && $mapping['type'] === 'collection') { + $mapping['strategy'] = isset($mapping['strategy']) ? $mapping['strategy'] : 'pushAll'; + } + if (isset($mapping['index'])) { + $keys = array( + $name => isset($mapping['order']) ? $mapping['order'] : 'asc' + ); + } + if (isset($mapping['unique'])) { + $keys = array( + $name => isset($mapping['order']) ? $mapping['order'] : 'asc' + ); + } + if ($keys !== null) { + $options = array(); + if (isset($mapping['index-name'])) { + $options['name'] = (string) $mapping['index-name']; + } + if (isset($mapping['drop-dups'])) { + $options['dropDups'] = (boolean) $mapping['drop-dups']; + } + if (isset($mapping['background'])) { + $options['background'] = (boolean) $mapping['background']; + } + if (isset($mapping['safe'])) { + $options['safe'] = (boolean) $mapping['safe']; + } + if (isset($mapping['unique'])) { + $options['unique'] = (boolean) $mapping['unique']; + } + if (isset($mapping['sparse'])) { + $options['sparse'] = (boolean) $mapping['sparse']; + } + $class->addIndex($keys, $options); + } + $class->mapField($mapping); + } + + private function addEmbedMapping(ClassMetadataInfo $class, $embed, $type) + { + $cascade = array_keys((array) $embed->cascade); + if (1 === count($cascade)) { + $cascade = current($cascade) ?: next($cascade); + } + $attributes = $embed->attributes(); + $mapping = array( + 'type' => $type, + 'embedded' => true, + 'targetDocument' => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null, + 'name' => (string) $attributes['field'], + 'strategy' => isset($attributes['strategy']) ? (string) $attributes['strategy'] : 'pushAll', + ); + if (isset($attributes['fieldName'])) { + $mapping['fieldName'] = (string) $attributes['fieldName']; + } + if (isset($embed->{'discriminator-field'})) { + $attr = $embed->{'discriminator-field'}; + $mapping['discriminatorField'] = (string) $attr['name']; + } + if (isset($embed->{'discriminator-map'})) { + foreach ($embed->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) { + $attr = $discriminatorMapping->attributes(); + $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class']; + } + } + $this->addFieldMapping($class, $mapping); + } + + private function addReferenceMapping(ClassMetadataInfo $class, $reference, $type) + { + $cascade = array_keys((array) $reference->cascade); + if (1 === count($cascade)) { + $cascade = current($cascade) ?: next($cascade); + } + $attributes = $reference->attributes(); + $mapping = array( + 'cascade' => $cascade, + 'type' => $type, + 'reference' => true, + 'simple' => isset($attributes['simple']) ? (boolean) $attributes['simple'] : false, + 'targetDocument' => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null, + 'name' => (string) $attributes['field'], + 'strategy' => isset($attributes['strategy']) ? (string) $attributes['strategy'] : 'pushAll', + 'inversedBy' => isset($attributes['inversed-by']) ? (string) $attributes['inversed-by'] : null, + 'mappedBy' => isset($attributes['mapped-by']) ? (string) $attributes['mapped-by'] : null, + 'repositoryMethod' => isset($attributes['repository-method']) ? (string) $attributes['repository-method'] : null, + 'limit' => isset($attributes['limit']) ? (integer) $attributes['limit'] : null, + 'skip' => isset($attributes['skip']) ? (integer) $attributes['skip'] : null, + ); + + if (isset($attributes['fieldName'])) { + $mapping['fieldName'] = (string) $attributes['fieldName']; + } + if (isset($reference->{'discriminator-field'})) { + $attr = $reference->{'discriminator-field'}; + $mapping['discriminatorField'] = (string) $attr['name']; + } + if (isset($reference->{'discriminator-map'})) { + foreach ($reference->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) { + $attr = $discriminatorMapping->attributes(); + $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class']; + } + } + if (isset($reference->{'sort'})) { + foreach ($reference->{'sort'}->{'sort'} as $sort) { + $attr = $sort->attributes(); + $mapping['sort'][(string) $attr['field']] = isset($attr['order']) ? (string) $attr['order'] : 'asc'; + } + } + if (isset($reference->{'criteria'})) { + foreach ($reference->{'criteria'}->{'criteria'} as $criteria) { + $attr = $criteria->attributes(); + $mapping['criteria'][(string) $attr['field']] = (string) $attr['value']; + } + } + $this->addFieldMapping($class, $mapping); + } + + private function addIndex(ClassMetadataInfo $class, \SimpleXmlElement $xmlIndex) + { + $attributes = $xmlIndex->attributes(); + $options = array(); + if (isset($attributes['name'])) { + $options['name'] = (string) $attributes['name']; + } + if (isset($attributes['drop-dups'])) { + $options['dropDups'] = ((string) $attributes['dropDups'] == 'false') ? false : true; + } + if (isset($attributes['background'])) { + $options['background'] = ((string) $attributes['background'] == 'false') ? false : true; + } + if (isset($attributes['safe'])) { + $options['safe'] = ((string) $attributes['safe'] == 'false') ? false : true; + } + if (isset($attributes['unique'])) { + $options['unique'] = ((string) $attributes['unique'] == 'false') ? false : true; + } + if (isset($attributes['sparse'])) { + $options['sparse'] = ((string) $attributes['sparse'] == 'false') ? false : true; + } + $index = array( + 'keys' => array(), + 'options' => $options + ); + foreach ($xmlIndex->{'key'} as $key) { + $index['keys'][(string) $key['name']] = isset($key['order']) ? (string) $key['order'] : 'asc'; + } + if (isset($xmlIndex->{'option'})) { + foreach ($xmlIndex->{'option'} as $option) { + $value = (string) $option['value']; + $value = $value === 'true' ? true : $value; + $value = $value === 'false' ? false : $value; + $index['options'][(string) $option['name']] = $value; + } + } + $class->addIndex($index['keys'], $index['options']); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $result = array(); + $xmlElement = simplexml_load_file($file); + + foreach (array('document', 'embedded-document', 'mapped-superclass') as $type) { + if (isset($xmlElement->$type)) { + foreach ($xmlElement->$type as $documentElement) { + $documentName = (string) $documentElement['name']; + $result[$documentName] = $documentElement; + } + } + } + + return $result; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/YamlDriver.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/YamlDriver.php new file mode 100644 index 00000000..4ff8db9a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/YamlDriver.php @@ -0,0 +1,258 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; +use Symfony\Component\Yaml\Yaml; + +/** + * The YamlDriver reads the mapping metadata from yaml schema files. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class YamlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $class) + { + /* @var $class ClassMetadataInfo */ + $element = $this->getElement($className); + if ( ! $element) { + return; + } + $element['type'] = isset($element['type']) ? $element['type'] : 'document'; + + if (isset($element['db'])) { + $class->setDatabase($element['db']); + } + if (isset($element['collection'])) { + $class->setCollection($element['collection']); + } + if ($element['type'] == 'document') { + if (isset($element['repositoryClass'])) { + $class->setCustomRepositoryClass($element['repositoryClass']); + } + } elseif ($element['type'] === 'mappedSuperclass') { + $class->isMappedSuperclass = true; + } elseif ($element['type'] === 'embeddedDocument') { + $class->isEmbeddedDocument = true; + } + if (isset($element['indexes'])) { + foreach($element['indexes'] as $index) { + $class->addIndex($index['keys'], isset($index['options']) ? $index['options'] : array()); + } + } + if (isset($element['inheritanceType'])) { + $class->setInheritanceType(constant('Doctrine\ODM\MongoDB\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType']))); + } + if (isset($element['discriminatorField'])) { + $class->setDiscriminatorField(array_intersect_key( + $element['discriminatorField'], + array('fieldName' => 1, 'name' => 1) + )); + } + if (isset($element['discriminatorMap'])) { + $class->setDiscriminatorMap($element['discriminatorMap']); + } + if (isset($element['changeTrackingPolicy'])) { + $class->setChangeTrackingPolicy(constant('Doctrine\ODM\MongoDB\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper($element['changeTrackingPolicy']))); + } + if (isset($element['requireIndexes'])) { + $class->setRequireIndexes($element['requireIndexes']); + } + if (isset($element['slaveOkay'])) { + $class->setSlaveOkay($element['slaveOkay']); + } + if (isset($element['fields'])) { + foreach ($element['fields'] as $fieldName => $mapping) { + if (is_string($mapping)) { + $type = $mapping; + $mapping = array(); + $mapping['type'] = $type; + } + if ( ! isset($mapping['fieldName'])) { + $mapping['fieldName'] = $fieldName; + } + if (isset($mapping['type']) && $mapping['type'] === 'collection') { + $mapping['strategy'] = isset($mapping['strategy']) ? $mapping['strategy'] : 'pushAll'; + } + if (isset($mapping['type']) && !empty($mapping['embedded'])) { + $this->addMappingFromEmbed($class, $fieldName, $mapping, $mapping['type']); + } elseif (isset($mapping['type']) && !empty($mapping['reference'])) { + $this->addMappingFromReference($class, $fieldName, $mapping, $mapping['type']); + } else { + $this->addFieldMapping($class, $mapping); + } + } + } + if (isset($element['embedOne'])) { + foreach ($element['embedOne'] as $fieldName => $embed) { + $this->addMappingFromEmbed($class, $fieldName, $embed, 'one'); + } + } + if (isset($element['embedMany'])) { + foreach ($element['embedMany'] as $fieldName => $embed) { + $this->addMappingFromEmbed($class, $fieldName, $embed, 'many'); + } + } + if (isset($element['referenceOne'])) { + foreach ($element['referenceOne'] as $fieldName => $reference) { + $this->addMappingFromReference($class, $fieldName, $reference, 'one'); + } + } + if (isset($element['referenceMany'])) { + foreach ($element['referenceMany'] as $fieldName => $reference) { + $this->addMappingFromReference($class, $fieldName, $reference, 'many'); + } + } + if (isset($element['lifecycleCallbacks'])) { + foreach ($element['lifecycleCallbacks'] as $type => $methods) { + foreach ($methods as $method) { + $class->addLifecycleCallback($method, constant('Doctrine\ODM\MongoDB\Events::' . $type)); + } + } + } + } + + private function addFieldMapping(ClassMetadataInfo $class, $mapping) + { + if (isset($mapping['name'])) { + $name = $mapping['name']; + } elseif (isset($mapping['fieldName'])) { + $name = $mapping['fieldName']; + } else { + throw new \InvalidArgumentException('Cannot infer a MongoDB name from the mapping'); + } + + $class->mapField($mapping); + + if ( ! (isset($mapping['index']) || isset($mapping['unique']) || isset($mapping['sparse']))) { + return; + } + + // Index this field if either "index", "unique", or "sparse" are set + $keys = array($name => 'asc'); + + if (isset($mapping['index']['order'])) { + $keys[$name] = $mapping['index']['order']; + unset($mapping['index']['order']); + } elseif (isset($mapping['unique']['order'])) { + $keys[$name] = $mapping['unique']['order']; + unset($mapping['unique']['order']); + } elseif (isset($mapping['sparse']['order'])) { + $keys[$name] = $mapping['sparse']['order']; + unset($mapping['sparse']['order']); + } + + $options = array(); + + if (isset($mapping['index'])) { + $options = is_array($mapping['index']) ? $mapping['index'] : array(); + } elseif (isset($mapping['unique'])) { + $options = is_array($mapping['unique']) ? $mapping['unique'] : array(); + $options['unique'] = true; + } elseif (isset($mapping['sparse'])) { + $options = is_array($mapping['sparse']) ? $mapping['sparse'] : array(); + $options['sparse'] = true; + } + + $class->addIndex($keys, $options); + } + + private function addMappingFromEmbed(ClassMetadataInfo $class, $fieldName, $embed, $type) + { + $mapping = array( + 'type' => $type, + 'embedded' => true, + 'targetDocument' => isset($embed['targetDocument']) ? $embed['targetDocument'] : null, + 'fieldName' => $fieldName, + 'strategy' => isset($embed['strategy']) ? (string) $embed['strategy'] : 'pushAll', + ); + if (isset($embed['name'])) { + $mapping['name'] = $embed['name']; + } + if (isset($embed['discriminatorField'])) { + $mapping['discriminatorField'] = $embed['discriminatorField']; + } + if (isset($embed['discriminatorMap'])) { + $mapping['discriminatorMap'] = $embed['discriminatorMap']; + } + $this->addFieldMapping($class, $mapping); + } + + private function addMappingFromReference(ClassMetadataInfo $class, $fieldName, $reference, $type) + { + $mapping = array( + 'cascade' => isset($reference['cascade']) ? $reference['cascade'] : null, + 'type' => $type, + 'reference' => true, + 'simple' => isset($reference['simple']) ? (boolean) $reference['simple'] : false, + 'targetDocument' => isset($reference['targetDocument']) ? $reference['targetDocument'] : null, + 'fieldName' => $fieldName, + 'strategy' => isset($reference['strategy']) ? (string) $reference['strategy'] : 'pushAll', + 'inversedBy' => isset($reference['inversedBy']) ? (string) $reference['inversedBy'] : null, + 'mappedBy' => isset($reference['mappedBy']) ? (string) $reference['mappedBy'] : null, + 'repositoryMethod' => isset($reference['repositoryMethod']) ? (string) $reference['repositoryMethod'] : null, + 'limit' => isset($reference['limit']) ? (integer) $reference['limit'] : null, + 'skip' => isset($reference['skip']) ? (integer) $reference['skip'] : null, + ); + if (isset($reference['name'])) { + $mapping['name'] = $reference['name']; + } + if (isset($reference['discriminatorField'])) { + $mapping['discriminatorField'] = $reference['discriminatorField']; + } + if (isset($reference['discriminatorMap'])) { + $mapping['discriminatorMap'] = $reference['discriminatorMap']; + } + if (isset($reference['sort'])) { + $mapping['sort'] = $reference['sort']; + } + if (isset($reference['criteria'])) { + $mapping['criteria'] = $reference['criteria']; + } + $this->addFieldMapping($class, $mapping); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return Yaml::parse($file); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php new file mode 100644 index 00000000..7f9fd118 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping; + +use Doctrine\Common\Persistence\Mapping\MappingException as BaseMappingException; + +/** + * Class for all exceptions related to the Doctrine MongoDB ODM + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class MappingException extends BaseMappingException +{ + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + public static function typeNotFound($name) + { + return new self('Type to be overwritten '.$name.' does not exist.'); + } + + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' in class '$className'."); + } + + public static function duplicateFieldMapping($document, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$document.'" was already declared, but it must be declared only once'); + } + + /** + * Throws an exception that indicates that a class used in a discriminator map does not exist. + * An example would be an outdated (maybe renamed) classname. + * + * @param string $className The class that could not be found + * @param string $owningClass The class that declares the discriminator map. + * @return self + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { + return new self( + "Document class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + public static function missingFieldName($className) + { + return new self("The Document class '$className' field mapping misses the 'fieldName' attribute."); + } + + public static function classIsNotAValidDocument($className) + { + return new self('Class '.$className.' is not a valid document or mapped super class.'); + } + + /** + * Exception for reflection exceptions - adds the document name, + * because there might be long classnames that will be shortened + * within the stacktrace + * + * @param string $document The document's name + * @param \ReflectionException $previousException + */ + public static function reflectionFailure($document, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $document, 0, $previousException); + } + + public static function identifierRequired($documentName) + { + return new self("No identifier/primary key specified for Document '$documentName'." + . " Every Document must have an identifier/primary key."); + } + + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } + + public static function missingIdGeneratorClass($className) + { + return new self("The class-option for the custom ID generator is missing in class $className."); + } + + public static function classIsNotAValidGenerator($className) + { + return new self("The class $className if not a valid ID generator of type AbstractIdGenerator."); + } + + public static function missingGeneratorSetter($className, $optionName) + { + return new self("The class $className is missing a setter for the option $optionName."); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataCustomType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataCustomType.php new file mode 100644 index 00000000..6fccfbc8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataCustomType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The BinDataCustom type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BinDataCustomType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? new \MongoBinData($value, \MongoBinData::CUSTOM) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value->bin : null; + } + + public function closureToMongo() + { + return '$return = $value !== null ? new \MongoBinData($value, \MongoBinData::CUSTOM) : null;'; + } + + public function closureToPHP() + { + return '$return = $value !== null ? $value->bin : null;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataFuncType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataFuncType.php new file mode 100644 index 00000000..105b3d8d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataFuncType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The BinDataFunc type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BinDataFuncType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? new \MongoBinData($value, \MongoBinData::FUNC) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value->bin : null; + } + + public function closureToMongo() + { + return '$return = $value !== null ? new \MongoBinData($value, \MongoBinData::FUNC) : null;'; + } + + public function closureToPHP() + { + return '$return = $value !== null ? $value->bin : null;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataMD5Type.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataMD5Type.php new file mode 100644 index 00000000..34e25ffb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataMD5Type.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The BinDataMD5 type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BinDataMD5Type extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? new \MongoBinData($value, \MongoBinData::MD5) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value->bin : null; + } + + public function closureToMongo() + { + return '$return = $value !== null ? new \MongoBinData($value, \MongoBinData::MD5) : null;'; + } + + public function closureToPHP() + { + return '$return = $value !== null ? $value->bin : null;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataType.php new file mode 100644 index 00000000..2c7bd45e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The BinData type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BinDataType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? new \MongoBinData($value, \MongoBinData::BYTE_ARRAY) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value->bin : null; + } + + public function closureToMongo() + { + return '$return = $value !== null ? new \MongoBinData($value, \MongoBinData::BYTE_ARRAY) : null;'; + } + + public function closureToPHP() + { + return '$return = $value !== null ? $value->bin : null;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataUUIDType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataUUIDType.php new file mode 100644 index 00000000..fb636a7f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BinDataUUIDType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The BinDataUUID type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BinDataUUIDType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? new \MongoBinData($value, \MongoBinData::UUID) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value->bin : null; + } + + public function closureToMongo() + { + return '$return = $value !== null ? new \MongoBinData($value, \MongoBinData::UUID) : null;'; + } + + public function closureToPHP() + { + return '$return = $value !== null ? $value->bin : null;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BooleanType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BooleanType.php new file mode 100644 index 00000000..da4a8928 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/BooleanType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Boolean type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class BooleanType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (boolean) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (boolean) $value : null; + } + + public function closureToMongo() + { + return '$return = (bool) $value;'; + } + + public function closureToPHP() + { + return '$return = (bool) $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CollectionType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CollectionType.php new file mode 100644 index 00000000..9556a2c3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CollectionType.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Collection type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Bulat Shakirzyanov + */ +class CollectionType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? array_values($value) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? array_values($value) : null; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CustomIdType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CustomIdType.php new file mode 100644 index 00000000..c1a0993b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/CustomIdType.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Id type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Bulat Shakirzyanov + */ +class CustomIdType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? $value : null; + } + + public function closureToMongo() + { + return '$return = $value;'; + } + + public function closureToPHP() + { + return '$return = $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/DateType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/DateType.php new file mode 100644 index 00000000..f9d6bc28 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/DateType.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Date type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class DateType extends Type +{ + public function convertToDatabaseValue($value) + { + if ($value === null) { + return null; + } + if ($value instanceof \MongoDate) { + return $value; + } + $timestamp = false; + if ($value instanceof \DateTime) { + $timestamp = $value->format('U'); + } elseif (is_numeric($value)) { + $timestamp = $value; + } elseif (is_string($value)) { + $timestamp = strtotime($value); + } + // Could not convert date to timestamp so store ISO 8601 formatted date instead + if ($timestamp === false) { + $date = new \DateTime($value); + return $date->format('c'); + } else { + $value = $timestamp; + } + return new \MongoDate($value); + } + + public function convertToPHPValue($value) + { + if ($value === null) { + return null; + } + if ($value instanceof \MongoDate) { + $date = new \DateTime(); + $date->setTimestamp($value->sec); + } else { + $date = new \DateTime($value); + } + return $date; + } + + public function closureToMongo() + { + return 'if ($value instanceof \DateTime) { $value = $value->getTimestamp(); } else if (is_string($value)) { $value = strtotime($value); } $return = new \MongoDate($value);'; + } + + public function closureToPHP() + { + return 'if ($value instanceof \MongoDate) { $date = new \DateTime(); $date->setTimestamp($value->sec); $return = $date; } else { $return = new \DateTime($value); }'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FileType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FileType.php new file mode 100644 index 00000000..826d1cd3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FileType.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The File type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class FileType extends Type +{ +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FloatType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FloatType.php new file mode 100644 index 00000000..4721738b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/FloatType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Float type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class FloatType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (float) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (float) $value : null; + } + + public function closureToMongo() + { + return '$return = (float) $value;'; + } + + public function closureToPHP() + { + return '$return = (float) $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/HashType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/HashType.php new file mode 100644 index 00000000..359e4c58 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/HashType.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Array type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Bulat Shakirzyanov + */ +class HashType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (object) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (array) $value : null; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IdType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IdType.php new file mode 100644 index 00000000..48914d42 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IdType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Id type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class IdType extends Type +{ + public function convertToDatabaseValue($value) + { + if ($value === null) { + return null; + } + if ( ! $value instanceof \MongoId) { + $value = new \MongoId($value); + } + return $value; + } + + public function convertToPHPValue($value) + { + return $value instanceof \MongoId ? (string) $value : $value; + } + + public function closureToMongo() + { + return '$return = new MongoId($value);'; + } + + public function closureToPHP() + { + return '$return = $value instanceof \MongoId ? (string) $value : $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IncrementType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IncrementType.php new file mode 100644 index 00000000..c27d455e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IncrementType.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Increment type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class IncrementType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (integer) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (integer) $value : null; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IntType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IntType.php new file mode 100644 index 00000000..604ac183 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/IntType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Int type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class IntType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (integer) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (integer) $value : null; + } + + public function closureToMongo() + { + return '$return = (int) $value;'; + } + + public function closureToPHP() + { + return '$return = (int) $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/KeyType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/KeyType.php new file mode 100644 index 00000000..b3eb3d3a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/KeyType.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Key type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class KeyType extends Type +{ + public function convertToDatabaseValue($value) + { + if ($value === null) { + return null; + } + return $value ? new \MongoMaxKey : new \MongoMinKey; + } + + public function convertToPHPValue($value) + { + if ($value === null) { + return null; + } + return $value instanceof \MongoMaxKey ? 1 : 0; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/ObjectIdType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/ObjectIdType.php new file mode 100644 index 00000000..5dc8d55c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/ObjectIdType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The ObjectId type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class ObjectIdType extends Type +{ + public function convertToDatabaseValue($value) + { + if ($value === null) { + return null; + } + if ( ! $value instanceof \MongoId) { + $value = new \MongoId($value); + } + return $value; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (string) $value : null; + } + + public function closureToMongo() + { + return '$return = new MongoId($value);'; + } + + public function closureToPHP() + { + return '$return = (string) $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/RawType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/RawType.php new file mode 100644 index 00000000..9e65512f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/RawType.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * Raw data type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Bulat Shakirzyanov + */ +class RawType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value; + } + + public function convertToPHPValue($value) + { + return $value; + } + + public function closureToMongo() + { + return '$return = $value;'; + } + + public function closureToPHP() + { + return '$return = $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/StringType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/StringType.php new file mode 100644 index 00000000..e4387fd2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/StringType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The String type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class StringType extends Type +{ + public function convertToDatabaseValue($value) + { + return $value !== null ? (string) $value : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (string) $value : null; + } + + public function closureToMongo() + { + return '$return = (string) $value;'; + } + + public function closureToPHP() + { + return '$return = (string) $value;'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/TimestampType.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/TimestampType.php new file mode 100644 index 00000000..e53d93d1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/TimestampType.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +/** + * The Timestamp type. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class TimestampType extends Type +{ + public function convertToDatabaseValue($value) + { + if ($value instanceof \MongoTimestamp) { + return $value; + } + + return $value !== null ? new \MongoTimestamp($value) : null; + } + + public function convertToPHPValue($value) + { + return $value !== null ? (string) $value : null; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/Type.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/Type.php new file mode 100644 index 00000000..32185a02 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Types/Type.php @@ -0,0 +1,218 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Mapping\Types; + +use Doctrine\ODM\MongoDB\Mapping\MappingException; + +/** + * The Type interface. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class Type +{ + /** + * Array of string types mapped to their type class. + */ + private static $typesMap = array( + 'id' => 'Doctrine\ODM\MongoDB\Mapping\Types\IdType', + 'custom_id' => 'Doctrine\ODM\MongoDB\Mapping\Types\CustomIdType', + 'boolean' => 'Doctrine\ODM\MongoDB\Mapping\Types\BooleanType', + 'int' => 'Doctrine\ODM\MongoDB\Mapping\Types\IntType', + 'float' => 'Doctrine\ODM\MongoDB\Mapping\Types\FloatType', + 'string' => 'Doctrine\ODM\MongoDB\Mapping\Types\StringType', + 'date' => 'Doctrine\ODM\MongoDB\Mapping\Types\DateType', + 'key' => 'Doctrine\ODM\MongoDB\Mapping\Types\KeyType', + 'timestamp' => 'Doctrine\ODM\MongoDB\Mapping\Types\TimestampType', + 'bin' => 'Doctrine\ODM\MongoDB\Mapping\Types\BinDataType', + 'bin_func' => 'Doctrine\ODM\MongoDB\Mapping\Types\BinDataFuncType', + 'bin_uuid' => 'Doctrine\ODM\MongoDB\Mapping\Types\BinDataUUIDType', + 'bin_md5' => 'Doctrine\ODM\MongoDB\Mapping\Types\BinDataMD5Type', + 'bin_custom' => 'Doctrine\ODM\MongoDB\Mapping\Types\BinDataCustomType', + 'file' => 'Doctrine\ODM\MongoDB\Mapping\Types\FileType', + 'hash' => 'Doctrine\ODM\MongoDB\Mapping\Types\HashType', + 'collection' => 'Doctrine\ODM\MongoDB\Mapping\Types\CollectionType', + 'increment' => 'Doctrine\ODM\MongoDB\Mapping\Types\IncrementType', + 'object_id' => 'Doctrine\ODM\MongoDB\Mapping\Types\ObjectIdType', + 'raw' => 'Doctrine\ODM\MongoDB\Mapping\Types\RawType' + ); + + /** + * Array of instantiated type classes. + */ + private static $types = array(); + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value) + { + return $value; + } + + public function closureToMongo() + { + return '$return = $value;'; + } + + public function closureToPHP() + { + return '$return = $value;'; + } + + /** + * Register a new type in the type map. + * + * @param string $name The name of the type. + * @param string $class The class name. + */ + public static function registerType($name, $class) + { + self::$typesMap[$name] = $class; + } + + /** + * Get a Type instance. + * + * @param string $type The type name. + * @return Doctrine\ODM\MongoDB\Mapping\Types\Type $type + * @throws InvalidArgumentException + */ + public static function getType($type) + { + if ( ! isset(self::$typesMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid type specified "%s".', $type)); + } + if ( ! isset(self::$types[$type])) { + $className = self::$typesMap[$type]; + self::$types[$type] = new $className; + } + return self::$types[$type]; + } + + /** + * Get a Type instance based on the type of the passed php variable. + * + * @param mixed $variable + * @return Doctrine\ODM\MongoDB\Mapping\Types\Type $type + * @throws InvalidArgumentException + */ + public static function getTypeFromPHPVariable($variable) + { + if (is_object($variable)) { + if ($variable instanceof \DateTime) { + return self::getType('date'); + } else if ($variable instanceof \MongoId) { + return self::getType('id'); + } + } else { + $type = gettype($variable); + switch ($type) { + case 'integer'; + return self::getType('int'); + } + } + return null; + } + + public static function convertPHPToDatabaseValue($value) + { + $type = self::getTypeFromPHPVariable($value); + if ($type !== null) { + return $type->convertToDatabaseValue($value); + } + return $value; + } + + /** + * Adds a custom type to the type map. + * + * @static + * @param string $name Name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * @throws MappingException + */ + public static function addType($name, $className) + { + if (isset(self::$typesMap[$name])) { + throw MappingException::typeExists($name); + } + + self::$typesMap[$name] = $className; + } + + /** + * Checks if exists support for a type. + * + * @static + * @param string $name Name of the type + * @return boolean TRUE if type is supported; FALSE otherwise + */ + public static function hasType($name) + { + return isset(self::$typesMap[$name]); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @static + * @param string $name + * @param string $className + * @throws MappingException + */ + public static function overrideType($name, $className) + { + if ( ! isset(self::$typesMap[$name])) { + throw MappingException::typeNotFound($name); + } + + self::$typesMap[$name] = $className; + } + + /** + * Get the types array map which holds all registered types and the corresponding + * type class + * + * @return array $typesMap + */ + public static function getTypesMap() + { + return self::$typesMap; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/MongoDBException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/MongoDBException.php new file mode 100644 index 00000000..f2d7ba60 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/MongoDBException.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * Class for all exceptions related to the Doctrine MongoDB ODM + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class MongoDBException extends \Exception +{ + public static function invalidFindByCall($documentName, $fieldName, $method) + { + return new self(sprintf('Invalid find by call %s::$fieldName (%s)', $documentName, $fieldName, $method)); + } + + public static function detachedDocumentCannotBeRemoved() + { + return new self('Detached document cannot be removed'); + } + + public static function invalidDocumentState($state) + { + return new self(sprintf('Invalid document state "%s"', $state)); + } + + public static function documentNotMappedToCollection($className) + { + return new self(sprintf('The "%s" document is not mapped to a MongoDB database collection.', $className)); + } + + public static function documentManagerClosed() + { + return new self('The DocumentManager is closed.'); + } + + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + public static function unknownDocumentNamespace($documentNamespaceAlias) + { + return new self("Unknown Document namespace alias '$documentNamespaceAlias'."); + } + + public static function cannotPersistMappedSuperclass($className) + { + return new self('Cannot persist an embedded document or mapped superclass ' . $className); + } + + public static function queryNotIndexed($className, $unindexedFields) + { + return new self(sprintf('Cannot execute unindexed queries on %s. Unindexed fields: %s', + $className, + implode(', ', $unindexedFields) + )); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/PersistentCollection.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/PersistentCollection.php new file mode 100644 index 00000000..ce55e506 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/PersistentCollection.php @@ -0,0 +1,680 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\Common\Collections\Collection as BaseCollection; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Proxy\Proxy; + +/** + * A PersistentCollection represents a collection of elements that have persistent state. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PersistentCollection implements BaseCollection +{ + /** + * A snapshot of the collection at the moment it was fetched from the database. + * This is used to create a diff of the collection at commit time. + * + * @var array + */ + private $snapshot = array(); + + private $owner; + + private $mapping; + + /** + * Whether the collection is dirty and needs to be synchronized with the database + * when the UnitOfWork that manages its persistent state commits. + * + * @var boolean + */ + private $isDirty = false; + + /** + * Whether the collection has already been initialized. + * + * @var boolean + */ + private $initialized = true; + + /** + * The wrapped Collection instance. + * + * @var Collection + */ + private $coll; + + /** + * The DocumentManager that manages the persistence of the collection. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The UnitOfWork that manages the persistence of the collection. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $uow; + + /** + * Mongo command prefix + * @var string + */ + private $cmd; + + /** + * The raw mongo data that will be used to initialize this collection. + * + * @var array + */ + private $mongoData = array(); + + /** + * Any hints to account for during reconstitution/lookup of the documents. + * + * @var array + */ + private $hints = array(); + + public function __construct(BaseCollection $coll, DocumentManager $dm, UnitOfWork $uow, $cmd) + { + $this->coll = $coll; + $this->dm = $dm; + $this->uow = $uow; + $this->cmd = $cmd; + } + + /** + * Sets the document manager and unit of work (used during merge operations). + * + * @param type $dm + */ + public function setDocumentManager(DocumentManager $dm) + { + $this->dm = $dm; + $this->uow = $dm->getUnitOfWork(); + } + + /** + * Sets the array of raw mongo data that will be used to initialize this collection. + * + * @param array $mongoData + */ + public function setMongoData(array $mongoData) + { + $this->mongoData = $mongoData; + } + + /** + * Gets the array of raw mongo data that will be used to initialize this collection. + * + * @return array $mongoData + */ + public function getMongoData() + { + return $this->mongoData; + } + + /** + * Set hints to account for during reconstitution/lookup of the documents. + * + * @param array $hints + */ + public function setHints(array $hints) + { + $this->hints = $hints; + } + + /** + * Get hints to account for during reconstitution/lookup of the documents. + * + * @return array $hints + */ + public function getHints() + { + return $this->hints; + } + + /** + * Initializes the collection by loading its contents from the database + * if the collection is not yet initialized. + */ + public function initialize() + { + if ( ! $this->initialized && $this->mapping) { + if ($this->isDirty) { + // Has NEW objects added through add(). Remember them. + $newObjects = $this->coll->toArray(); + } + $this->coll->clear(); + $this->uow->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if (isset($newObjects)) { + foreach ($newObjects as $key => $obj) { + if ($this->mapping['strategy'] === 'set') { + $this->coll->set($key, $obj); + } else { + $this->coll->add($obj); + } + } + $this->isDirty = true; + } + $this->mongoData = array(); + $this->initialized = true; + } + } + + /** + * Marks this collection as changed/dirty. + */ + private function changed() + { + if ($this->isDirty) { + return; + } + + $this->isDirty = true; + + if ($this->dm && + $this->mapping !== null && + $this->mapping['isOwningSide'] && + $this->owner && + $this->dm->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { + $this->uow->scheduleForDirtyCheck($this->owner); + } + } + + /** + * Gets a boolean flag indicating whether this collection is dirty which means + * its state needs to be synchronized with the database. + * + * @return boolean TRUE if the collection is dirty, FALSE otherwise. + */ + public function isDirty() + { + return $this->isDirty; + } + + /** + * Sets a boolean flag, indicating whether this collection is dirty. + * + * @param boolean $dirty Whether the collection should be marked dirty or not. + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * INTERNAL: + * Sets the collection's owning entity together with the AssociationMapping that + * describes the association between the owner and the elements of the collection. + * + * @param object $document + * @param AssociationMapping $mapping + */ + public function setOwner($document, array $mapping) + { + $this->owner = $document; + $this->mapping = $mapping; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + */ + public function takeSnapshot() + { + $this->snapshot = $this->coll->toArray(); + $this->isDirty = false; + } + + /** + * INTERNAL: + * Clears the internal snapshot information and sets isDirty to true if the collection + * has elements. + */ + public function clearSnapshot() + { + $this->snapshot = array(); + $this->isDirty = $this->count() ? true : false; + } + + /** + * INTERNAL: + * Returns the last snapshot of the elements in the collection. + * + * @return array The last snapshot of the elements. + */ + public function getSnapshot() + { + return $this->snapshot; + } + + /** + * INTERNAL: + * getDeleteDiff + * + * @return array + */ + public function getDeleteDiff() + { + return array_udiff_assoc($this->snapshot, $this->coll->toArray(), + function($a, $b) {return $a === $b ? 0 : 1;}); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc($this->coll->toArray(), $this->snapshot, + function($a, $b) {return $a === $b ? 0 : 1;}); + } + + /** + * INTERNAL: + * Gets the collection owner. + * + * @return object + */ + public function getOwner() + { + return $this->owner; + } + + public function getMapping() + { + return $this->mapping; + } + + public function getTypeClass() + { + return $this->typeClass; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * Checks whether this collection has been initialized. + * + * @return boolean + */ + public function isInitialized() + { + return $this->initialized; + } + + /** {@inheritdoc} */ + public function first() + { + $this->initialize(); + return $this->coll->first(); + } + + /** {@inheritdoc} */ + public function last() + { + $this->initialize(); + return $this->coll->last(); + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + $this->initialize(); + $removed = $this->coll->remove($key); + if ($removed) { + $this->changed(); + if ($this->mapping !== null && isset($this->mapping['embedded'])) { + $this->uow->scheduleOrphanRemoval($removed); + } + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function removeElement($element) + { + $this->initialize(); + $removed = $this->coll->removeElement($element); + if ($removed) { + $this->changed(); + if ($this->mapping !== null && isset($this->mapping['embedded'])) { + $this->uow->scheduleOrphanRemoval($element); + } + } + return $removed; + } + /** + * {@inheritdoc} + */ + public function containsKey($key) + { + $this->initialize(); + return $this->coll->containsKey($key); + } + + /** + * {@inheritdoc} + */ + public function contains($element) + { + $this->initialize(); + return $this->coll->contains($element); + } + + /** + * {@inheritdoc} + */ + public function exists(\Closure $p) + { + $this->initialize(); + return $this->coll->exists($p); + } + + /** + * {@inheritdoc} + */ + public function indexOf($element) + { + $this->initialize(); + return $this->coll->indexOf($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->initialize(); + return $this->coll->get($key); + } + + /** + * {@inheritdoc} + */ + public function getKeys() + { + $this->initialize(); + return $this->coll->getKeys(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + $this->initialize(); + return $this->coll->getValues(); + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->mapping['isInverseSide']) { + $this->initialize(); + } + return count($this->mongoData) + $this->coll->count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + $this->coll->set($key, $value); + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->coll->add($value); + $this->changed(); + return true; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + return $this->count() === 0 ? true : false; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $this->initialize(); + return $this->coll->getIterator(); + } + + /** + * {@inheritdoc} + */ + public function map(\Closure $func) + { + $this->initialize(); + return $this->coll->map($func); + } + + /** + * {@inheritdoc} + */ + public function filter(\Closure $p) + { + $this->initialize(); + return $this->coll->filter($p); + } + + /** + * {@inheritdoc} + */ + public function forAll(\Closure $p) + { + $this->initialize(); + return $this->coll->forAll($p); + } + + /** + * {@inheritdoc} + */ + public function partition(\Closure $p) + { + $this->initialize(); + return $this->coll->partition($p); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $this->initialize(); + return $this->coll->toArray(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + if ($this->initialized && $this->isEmpty()) { + return; + } + if ($this->mapping !== null && isset($this->mapping['embedded'])) { + foreach ($this->coll as $element) { + $this->uow->scheduleOrphanRemoval($element); + } + } + $this->mongoData = array(); + $this->coll->clear(); + if ($this->mapping['isOwningSide']) { + $this->changed(); + $this->uow->scheduleCollectionDeletion($this); + $this->takeSnapshot(); + } + } + + /** + * {@inheritdoc} + */ + public function slice($offset, $length = null) + { + $this->initialize(); + return $this->coll->slice($offset, $length); + } + + /** + * Called by PHP when this collection is serialized. Ensures that only the + * elements are properly serialized. + * + * @internal Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + */ + public function __sleep() + { + return array('coll', 'initialized'); + } + + /* ArrayAccess implementation */ + + /** + * @see containsKey() + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * @see get() + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * @see add() + * @see set() + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + return $this->set($offset, $value); + } + + /** + * @see remove() + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + public function key() + { + return $this->coll->key(); + } + + /** + * Gets the element of the collection at the current iterator position. + */ + public function current() + { + return $this->coll->current(); + } + + /** + * Moves the internal iterator position to the next element. + */ + public function next() + { + return $this->coll->next(); + } + + /** + * Retrieves the wrapped Collection instance. + */ + public function unwrap() + { + return $this->coll; + } + + /** + * Cleanup internal state of cloned persistent collection. + * + * The following problems have to be prevented: + * 1. Added documents are added to old PersistentCollection + * 2. New collection is not dirty, if reused on other document nothing + * changes. + * 3. Snapshot leads to invalid diffs being generated. + * 4. Lazy loading grabs entities from old owner object. + * 5. New collection is connected to old owner and leads to duplicate keys. + */ + public function __clone() + { + if (is_object($this->coll)) { + $this->coll = clone $this->coll; + } + + $this->initialize(); + + $this->owner = null; + $this->snapshot = array(); + + $this->changed(); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php new file mode 100644 index 00000000..9dc2f6e2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php @@ -0,0 +1,259 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Persisters; + +use Doctrine\ODM\MongoDB\PersistentCollection; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder; +use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * The CollectionPersister is responsible for persisting collections of embedded documents + * or referenced documents. When a PersistentCollection is scheduledForDeletion in the UnitOfWork + * by calling PersistentCollection::clear() or is de-referenced in the domain application + * code it results in a CollectionPersister::delete(). When a single document is removed + * from a PersitentCollection it is removed in the call to CollectionPersister::deleteRows() + * and new documents added to the PersistentCollection are inserted in the call to + * CollectionPersister::insertRows(). + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + * @author Roman Borschel + */ +class CollectionPersister +{ + /** + * The DocumentManager instance. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The PersistenceBuilder instance. + * + * @var Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder + */ + private $pb; + + /** + * Mongo command prefix + * + * @var string + */ + private $cmd; + + /** + * Contructs a new CollectionPersister instance. + * + * @param DocumentManager $dm + * @param PersistenceBuilder $pb + * @param UnitOfWork $uow + * @param string $cmd + */ + public function __construct(DocumentManager $dm, PersistenceBuilder $pb, UnitOfWork $uow, $cmd) + { + $this->dm = $dm; + $this->pb = $pb; + $this->uow = $uow; + $this->cmd = $cmd; + } + + /** + * Deletes a PersistentCollection instance completely from a document using $unset. + * + * @param PersistentCollection $coll + * @param array $options + */ + public function delete(PersistentCollection $coll, array $options) + { + $mapping = $coll->getMapping(); + if ($mapping['isInverseSide']) { + return; // ignore inverse side + } + list($propertyPath, $parent) = $this->getPathAndParent($coll); + $query = array($this->cmd . 'unset' => array($propertyPath => true)); + $this->executeQuery($parent, $query, $options); + } + + /** + * Updates a PersistentCollection instance deleting removed rows and inserting new rows. + * + * @param PersistentCollection $coll + * @param array $options + */ + public function update(PersistentCollection $coll, array $options) + { + $mapping = $coll->getMapping(); + if ($mapping['isInverseSide']) { + return; // ignore inverse side + } + $this->deleteRows($coll, $options); + $this->insertRows($coll, $options); + } + + /** + * Deletes removed rows from a PersistentCollection instance. + * + * @param PersistentCollection $coll + * @param array $options + */ + private function deleteRows(PersistentCollection $coll, array $options) + { + $deleteDiff = $coll->getDeleteDiff(); + if ($deleteDiff) { + list($propertyPath, $parent) = $this->getPathAndParent($coll); + $query = array($this->cmd.'unset' => array()); + $isAssocArray = false; + foreach ($deleteDiff as $key => $document) { + $isAssocArray |= !is_int($key); + $query[$this->cmd.'unset'][$propertyPath.'.'.$key] = true; + } + $this->executeQuery($parent, $query, $options); + + /** + * @todo This is a hack right now because we don't have a proper way to remove + * an element from an array by its key. Unsetting the key results in the element + * being left in the array as null so we have to pull null values. + * + * "Using "$unset" with an expression like this "array.$" will result in the array item becoming null, not being removed. You can issue an update with "{$pull:{x:null}}" to remove all nulls." + * http://www.mongodb.org/display/DOCS/Updating#Updating-%24unset + */ + $mapping = $coll->getMapping(); + if ($mapping['strategy'] !== 'set' || !$isAssocArray) { + $this->executeQuery($parent, array($this->cmd.'pull' => array($propertyPath => null)), $options); + } + } + } + + /** + * Inserts new rows for a PersistentCollection instance. + * + * @param PersistentCollection $coll + * @param array $options + */ + private function insertRows(PersistentCollection $coll, array $options) + { + $mapping = $coll->getMapping(); + list($propertyPath, $parent) = $this->getPathAndParent($coll); + if ($mapping['strategy'] === 'set') { + $setData = array(); + $insertDiff = $coll->getInsertDiff(); + if ($insertDiff) { + foreach ($insertDiff as $key => $document) { + if (isset($mapping['reference'])) { + $documentUpdates = $this->pb->prepareReferencedDocumentValue($mapping, $document); + } else { + $documentUpdates = $this->pb->prepareEmbeddedDocumentValue($mapping, $document); + } + + if (is_int($key)) { + $setData[$propertyPath][] = $documentUpdates; + } else { + foreach ($documentUpdates as $currFieldName => $currFieldValue) { + $setData[$propertyPath. '.' .$key . '.' . $currFieldName] = $currFieldValue; + } + } + } + + $query = array($this->cmd.'set' => $setData); + $this->executeQuery($parent, $query, $options); + } + } else { + $strategy = isset($mapping['strategy']) ? $mapping['strategy'] : 'pushAll'; + $insertDiff = $coll->getInsertDiff(); + if ($insertDiff) { + $query = array($this->cmd.$strategy => array()); + foreach ($insertDiff as $document) { + if (isset($mapping['reference'])) { + $query[$this->cmd.$strategy][$propertyPath][] = $this->pb->prepareReferencedDocumentValue($mapping, $document); + } else { + $query[$this->cmd.$strategy][$propertyPath][] = $this->pb->prepareEmbeddedDocumentValue($mapping, $document); + } + } + $this->executeQuery($parent, $query, $options); + } + } + } + + /** + * Gets the document database identifier value for the given document. + * + * @param object $document + * @param ClassMetadata $class + * @return mixed $id + */ + private function getDocumentId($document, ClassMetadata $class) + { + return $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document)); + } + + /** + * Gets the parent information for a given PersistentCollection. It will retrieve the top + * level persistent @Document that the PersistentCollection lives in. We can use this to issue + * queries when updating a PersistentCollection that is multiple levels deep inside an + * embedded document. + * + * + * list($path, $parent) = $this->getPathAndParent($coll) + * + * + * @param PersistentCollection $coll + * @return array $pathAndParent + */ + private function getPathAndParent(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $fields = array(); + $parent = $coll->getOwner(); + while (null !== ($association = $this->uow->getParentAssociation($parent))) { + list($m, $owner, $field) = $association; + if (isset($m['reference'])) { + break; + } + $parent = $owner; + $fields[] = $field; + } + $propertyPath = implode('.', array_reverse($fields)); + $path = $mapping['name']; + if ($propertyPath) { + $path = $propertyPath.'.'.$path; + } + return array($path, $parent); + } + + /** + * Executes a query updating the given document. + * + * @param object $document + * @param array $query + * @param array $options + */ + private function executeQuery($document, array $query, array $options) + { + $className = get_class($document); + $class = $this->dm->getClassMetadata($className); + $id = $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document)); + $collection = $this->dm->getDocumentCollection($className); + $collection->update(array('_id' => $id), $query, $options); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php new file mode 100644 index 00000000..edd007ec --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -0,0 +1,1031 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Persisters; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\Common\EventManager; +use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\Types\Type; +use Doctrine\Common\Collections\Collection; +use Doctrine\ODM\MongoDB\Events; +use Doctrine\ODM\MongoDB\Event\OnUpdatePreparedArgs; +use Doctrine\ODM\MongoDB\MongoDBException; +use Doctrine\ODM\MongoDB\LockException; +use Doctrine\ODM\MongoDB\PersistentCollection; +use Doctrine\ODM\MongoDB\Query\Query; +use Doctrine\MongoDB\ArrayIterator; +use Doctrine\ODM\MongoDB\Proxy\Proxy; +use Doctrine\ODM\MongoDB\LockMode; +use Doctrine\ODM\MongoDB\Cursor; +use Doctrine\ODM\MongoDB\LoggableCursor; +use Doctrine\MongoDB\Cursor as BaseCursor; +use Doctrine\MongoDB\Iterator; +use Doctrine\MongoDB\LoggableCursor as BaseLoggableCursor; + +/** + * The DocumentPersister is responsible for persisting documents. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +class DocumentPersister +{ + /** + * The PersistenceBuilder instance. + * + * @var Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder + */ + private $pb; + + /** + * The DocumentManager instance. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The EventManager instance + * + * @var Doctrine\Common\EventManager + */ + private $evm; + + /** + * The UnitOfWork instance. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $uow; + + /** + * The Hydrator instance + * + * @var Doctrine\ODM\MongoDB\Hydrator + */ + private $hydrator; + + /** + * The ClassMetadata instance for the document type being persisted. + * + * @var Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + private $class; + + /** + * The MongoCollection instance for this document. + * + * @var Doctrine\ODM\MongoDB\MongoCollection + */ + private $collection; + + /** + * Array of queued inserts for the persister to insert. + * + * @var array + */ + private $queuedInserts = array(); + + /** + * Documents to be updated, used in executeReferenceUpdates() method + * @var array + */ + private $documentsToUpdate = array(); + + /** + * Fields to update, used in executeReferenceUpdates() method + * @var array + */ + private $fieldsToUpdate = array(); + + /** + * Mongo command prefix + * + * @var string + */ + private $cmd; + + /** + * Initializes a new DocumentPersister instance. + * + * @param Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder $pb + * @param Doctrine\ODM\MongoDB\DocumentManager $dm + * @param Doctrine\Common\EventManager $evm + * @param Doctrine\ODM\MongoDB\UnitOfWork $uow + * @param Doctrine\ODM\MongoDB\Hydrator\HydratorFactory $hydratorFactory + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class + * @param string $cmd + */ + public function __construct(PersistenceBuilder $pb, DocumentManager $dm, EventManager $evm, UnitOfWork $uow, HydratorFactory $hydratorFactory, ClassMetadata $class, $cmd) + { + $this->pb = $pb; + $this->dm = $dm; + $this->evm = $evm; + $this->cmd = $cmd; + $this->uow = $uow; + $this->hydratorFactory = $hydratorFactory; + $this->class = $class; + $this->collection = $dm->getDocumentCollection($class->name); + } + + public function getInserts() + { + return $this->queuedInserts; + } + + public function isQueuedForInsert($document) + { + return isset($this->queuedInserts[spl_object_hash($document)]) ? true : false; + } + + /** + * Adds a document to the queued insertions. + * The document remains queued until {@link executeInserts} is invoked. + * + * @param object $document The document to queue for insertion. + */ + public function addInsert($document) + { + $this->queuedInserts[spl_object_hash($document)] = $document; + } + + /** + * Gets the ClassMetadata instance of the document class this persister is used for. + * + * @return Doctrine\ODM\MongoDB\Mapping\ClassMetadata + */ + public function getClassMetadata() + { + return $this->class; + } + + /** + * Executes all queued document insertions and returns any generated post-insert + * identifiers that were created as a result of the insertions. + * + * If no inserts are queued, invoking this method is a NOOP. + * + * @param array $options Array of options to be used with batchInsert() + * @return array An array of any generated post-insert IDs. This will be an empty array + * if the document class does not use the IDENTITY generation strategy. + */ + public function executeInserts(array $options = array()) + { + if ( ! $this->queuedInserts) { + return; + } + + $postInsertIds = array(); + $inserts = array(); + $upserts = array(); + foreach ($this->queuedInserts as $oid => $document) { + $upsert = $this->uow->isScheduledForUpsert($document); + if ($upsert) { + $data = $this->pb->prepareUpsertData($document); + } else { + $data = $this->pb->prepareInsertData($document); + } + + // Set the initial version for each insert + if ($this->class->isVersioned) { + $versionMapping = $this->class->fieldMappings[$this->class->versionField]; + if ($versionMapping['type'] === 'int') { + $currentVersion = $this->class->reflFields[$this->class->versionField]->getValue($document); + $data[$versionMapping['name']] = $currentVersion; + $this->class->reflFields[$this->class->versionField]->setValue($document, $currentVersion); + } elseif ($versionMapping['type'] === 'date') { + $nextVersion = new \DateTime(); + $data[$versionMapping['name']] = new \MongoDate($nextVersion->getTimestamp()); + $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersion); + } + } + + if ($upsert) { + $upserts[$oid] = $data; + } else { + $inserts[$oid] = $data; + } + } + if ($inserts) { + $this->collection->batchInsert($inserts, $options); + + foreach ($inserts as $oid => $data) { + $document = $this->queuedInserts[$oid]; + $postInsertIds[] = array($this->class->getPHPIdentifierValue($data['_id']), $document); + } + } + + if ($upserts) { + $upsertOptions = $options; + $upsertOptions['upsert'] = true; + foreach ($upserts as $oid => $data) { + $criteria = array('_id' => $data[$this->cmd.'set']['_id']); + unset($data[$this->cmd.'set']['_id']); + // stupid php + if (empty($data[$this->cmd.'set'])) { + $data[$this->cmd.'set'] = new \stdClass; + } + $this->collection->update($criteria, $data, $upsertOptions); + } + } + + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * Updates the already persisted document if it has any new changesets. + * + * @param object $document + * @param array $options Array of options to be used with update() + */ + public function update($document, array $options = array()) + { + $id = $this->uow->getDocumentIdentifier($document); + $update = $this->pb->prepareUpdateData($document); + + if ( ! empty($update)) { + + $id = $this->class->getDatabaseIdentifierValue($id); + $query = array('_id' => $id); + + // Include versioning logic to set the new version value in the database + // and to ensure the version has not changed since this document object instance + // was fetched from the database + if ($this->class->isVersioned) { + $versionMapping = $this->class->fieldMappings[$this->class->versionField]; + $currentVersion = $this->class->reflFields[$this->class->versionField]->getValue($document); + if ($versionMapping['type'] === 'int') { + $nextVersion = $currentVersion + 1; + $update[$this->cmd . 'inc'][$versionMapping['name']] = 1; + $query[$versionMapping['name']] = $currentVersion; + $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersion); + } elseif ($versionMapping['type'] === 'date') { + $nextVersion = new \DateTime(); + $update[$this->cmd . 'set'][$versionMapping['name']] = new \MongoDate($nextVersion->getTimestamp()); + $query[$versionMapping['name']] = new \MongoDate($currentVersion->getTimestamp()); + $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersion); + } + $options['safe'] = true; + } + + // Include locking logic so that if the document object in memory is currently + // locked then it will remove it, otherwise it ensures the document is not locked. + if ($this->class->isLockable) { + $isLocked = $this->class->reflFields[$this->class->lockField]->getValue($document); + $lockMapping = $this->class->fieldMappings[$this->class->lockField]; + if ($isLocked) { + $update[$this->cmd . 'unset'] = array($lockMapping['name'] => true); + } else { + $query[$lockMapping['name']] = array($this->cmd . 'exists' => false); + } + } + + unset($update[$this->cmd.'set']['_id']); + $result = $this->collection->update($query, $update, $options); + + if (($this->class->isVersioned || $this->class->isLockable) && ! $result['n']) { + throw LockException::lockFailed($document); + } + } + } + + /** + * Removes document from mongo + * + * @param mixed $document + * @param array $options Array of options to be used with remove() + */ + public function delete($document, array $options = array()) + { + $id = $this->uow->getDocumentIdentifier($document); + $query = array('_id' => $this->class->getDatabaseIdentifierValue($id)); + + if ($this->class->isLockable) { + $query[$this->class->lockField] = array($this->cmd . 'exists' => false); + $options['safe'] = true; + } + + $result = $this->collection->remove($query, $options); + + if (($this->class->isVersioned || $this->class->isLockable) && ! $result['n']) { + throw LockException::lockFailed($document); + } + } + + /** + * Refreshes a managed document. + * + * @param array $id The identifier of the document. + * @param object $document The document to refresh. + */ + public function refresh($id, $document) + { + $class = $this->dm->getClassMetadata(get_class($document)); + $data = $this->collection->findOne(array('_id' => $id)); + $data = $this->hydratorFactory->hydrate($document, $data); + $this->uow->setOriginalDocumentData($document, $data); + } + + /** + * Loads an document by a list of field criteria. + * + * @param array $criteria The criteria by which to load the document. + * @param object $document The document to load the data into. If not specified, + * a new document is created. + * @param array $hints Hints for document creation. + * @param int $lockMode + * @param array $sort + * @return object The loaded and managed document instance or NULL if the document can not be found. + * @todo Check identity map? loadById method? Try to guess whether $criteria is the id? + */ + public function load($criteria, $document = null, array $hints = array(), $lockMode = 0, array $sort = array()) + { + $criteria = $this->prepareQuery($criteria); + $cursor = $this->collection->find($criteria)->limit(1); + if ($sort) { + $cursor->sort($sort); + } + $result = $cursor->getSingleResult(); + + if ($this->class->isLockable) { + $lockMapping = $this->class->fieldMappings[$this->class->lockField]; + if (isset($result[$lockMapping['name']]) && $result[$lockMapping['name']] === LockMode::PESSIMISTIC_WRITE) { + throw LockException::lockFailed($result); + } + } + + return $this->createDocument($result, $document, $hints); + } + + /** + * Loads a list of documents by a list of field criteria. + * + * @param array $criteria + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $criteria = $this->prepareQuery($criteria); + $cursor = $this->collection->find($criteria); + + if (null !== $orderBy) { + $cursor->sort($orderBy); + } + + if (null !== $limit) { + $cursor->limit($limit); + } + + if (null !== $offset) { + $cursor->skip($offset); + } + + return $this->wrapCursor($cursor); + } + + /** + * Wraps the supplied base cursor as an ODM one. + * + * @param Doctrine\MongoDB\Cursor $cursor The base cursor + * + * @return Cursor An ODM cursor + */ + private function wrapCursor(BaseCursor $cursor) + { + if ($cursor instanceof BaseLoggableCursor) { + return new LoggableCursor( + $this->dm->getConnection(), + $this->collection, + $this->dm->getUnitOfWork(), + $this->class, + $cursor, + $cursor->getQuery(), + $cursor->getFields(), + $this->dm->getConfiguration()->getRetryQuery(), + $cursor->getLoggerCallable() + ); + } else { + return new Cursor( + $this->dm->getConnection(), + $this->collection, + $this->dm->getUnitOfWork(), + $this->class, + $cursor, + $cursor->getQuery(), + $cursor->getFields(), + $this->dm->getConfiguration()->getRetryQuery() + ); + } + } + + /** + * Checks whether the given managed document exists in the database. + * + * @param object $document + * @return boolean TRUE if the document exists in the database, FALSE otherwise. + */ + public function exists($document) + { + $id = $this->class->getIdentifierObject($document); + return (bool) $this->collection->findOne(array(array('_id' => $id)), array('_id')); + } + + /** + * Locks document by storing the lock mode on the mapped lock field. + * + * @param object $document + * @param int $lockMode + */ + public function lock($document, $lockMode) + { + $id = $this->uow->getDocumentIdentifier($document); + $criteria = array('_id' => $this->class->getDatabaseIdentifierValue($id)); + $lockMapping = $this->class->fieldMappings[$this->class->lockField]; + $this->collection->update($criteria, array($this->cmd.'set' => array($lockMapping['name'] => $lockMode))); + $this->class->reflFields[$this->class->lockField]->setValue($document, $lockMode); + } + + /** + * Releases any lock that exists on this document. + * + * @param object $document + */ + public function unlock($document) + { + $id = $this->uow->getDocumentIdentifier($document); + $criteria = array('_id' => $this->class->getDatabaseIdentifierValue($id)); + $lockMapping = $this->class->fieldMappings[$this->class->lockField]; + $this->collection->update($criteria, array($this->cmd.'unset' => array($lockMapping['name'] => true))); + $this->class->reflFields[$this->class->lockField]->setValue($document, null); + } + + /** + * Creates or fills a single document object from an query result. + * + * @param $result The query result. + * @param object $document The document object to fill, if any. + * @param array $hints Hints for document creation. + * @return object The filled and managed document object or NULL, if the query result is empty. + */ + private function createDocument($result, $document = null, array $hints = array()) + { + if ($result === null) { + return null; + } + + if ($document !== null) { + $hints[Query::HINT_REFRESH] = true; + $id = $this->class->getPHPIdentifierValue($result['_id']); + $this->uow->registerManaged($document, $id, $result); + } + + return $this->uow->getOrCreateDocument($this->class->name, $result, $hints); + } + + /** + * Prime a collection of documents property with an efficient single query instead of + * lazily loading each field with a single query. + * + * @param Iterator $collection + * @param string $fieldName + * @param Closure|boolean $primer + * @param array $hints + */ + public function primeCollection(Iterator $collection, $fieldName, $primer, array $hints = array()) + { + $collection = $collection->toArray(); + if (!count($collection)) { + return; + } + $collectionMetaData = $this->dm->getClassMetaData(get_class(current($collection))); + + $fieldMapping = $collectionMetaData->fieldMappings[$fieldName]; + + $cmd = $this->cmd; + $groupedIds = array(); + + foreach ($collection as $element) { + if ($fieldMapping['type'] == 'many') { + $fieldValue = $collectionMetaData->getFieldValue($element, $fieldName); + if ($fieldValue instanceof PersistentCollection) { + foreach ($fieldValue->getMongoData() as $key => $reference) { + if (isset($fieldMapping['simple']) && $fieldMapping['simple']) { + $className = $fieldMapping['targetDocument']; + $mongoId = $reference; + } else { + $className = $this->dm->getClassNameFromDiscriminatorValue($fieldMapping, $reference); + $mongoId = $reference[$cmd . 'id']; + } + $id = $this->dm->getClassMetadata($className)->getPHPIdentifierValue($mongoId); + $document = $this->uow->tryGetById($id, $className); + if (!$document || $document instanceof Proxy && ! $document->__isInitialized__) { + if ( ! isset($groupedIds[$className])) { + $groupedIds[$className] = array(); + } + $groupedIds[$className][$id] = $mongoId; + } + } + } + } else if ($fieldMapping['type'] == 'one') { + $document = $collectionMetaData->getFieldValue($element, $fieldName); + if ($document && $document instanceof Proxy && ! $document->__isInitialized__) { + $class = $this->dm->getClassMetadata(get_class($document)); + $id = $this->uow->getDocumentIdentifier($document); + $groupedIds[$class->name][$id] = $id; + } + } + } + foreach ($groupedIds as $className => $ids) { + $class = $this->dm->getClassMetadata($className); + if ($primer instanceof \Closure) { + $primer($this->dm, $className, $fieldName, $ids, $hints); + } else { + $repository = $this->dm->getRepository($className); + $qb = $repository->createQueryBuilder() + ->field($class->identifier)->in($ids); + if (isset($hints[Query::HINT_SLAVE_OKAY])) { + $qb->slaveOkay(true); + } + $query = $qb->getQuery(); + $query->execute()->toArray(); + } + } + } + + /** + * Loads a PersistentCollection data. Used in the initialize() method. + * + * @param PersistentCollection $collection + */ + public function loadCollection(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + switch ($mapping['association']) { + case ClassMetadata::EMBED_MANY: + $this->loadEmbedManyCollection($collection); + break; + + case ClassMetadata::REFERENCE_MANY: + if (isset($mapping['repositoryMethod']) && $mapping['repositoryMethod']) { + $this->loadReferenceManyWithRepositoryMethod($collection); + } else { + if ($mapping['isOwningSide']) { + $this->loadReferenceManyCollectionOwningSide($collection); + } else { + $this->loadReferenceManyCollectionInverseSide($collection); + } + } + break; + } + } + + private function loadEmbedManyCollection(PersistentCollection $collection) + { + $embeddedDocuments = $collection->getMongoData(); + $mapping = $collection->getMapping(); + $owner = $collection->getOwner(); + if ($embeddedDocuments) { + foreach ($embeddedDocuments as $key => $embeddedDocument) { + $className = $this->dm->getClassNameFromDiscriminatorValue($mapping, $embeddedDocument); + $embeddedMetadata = $this->dm->getClassMetadata($className); + $embeddedDocumentObject = $embeddedMetadata->newInstance(); + + $data = $this->hydratorFactory->hydrate($embeddedDocumentObject, $embeddedDocument); + $this->uow->registerManaged($embeddedDocumentObject, null, $data); + $this->uow->setParentAssociation($embeddedDocumentObject, $mapping, $owner, $mapping['name'].'.'.$key); + if ($mapping['strategy'] === 'set') { + $collection->set($key, $embeddedDocumentObject); + } else { + $collection->add($embeddedDocumentObject); + } + } + } + } + + private function loadReferenceManyCollectionOwningSide(PersistentCollection $collection) + { + $hints = $collection->getHints(); + $mapping = $collection->getMapping(); + $cmd = $this->cmd; + $groupedIds = array(); + + $sorted = isset($mapping['sort']) && $mapping['sort']; + + foreach ($collection->getMongoData() as $key => $reference) { + if (isset($mapping['simple']) && $mapping['simple']) { + $className = $mapping['targetDocument']; + $mongoId = $reference; + } else { + $className = $this->dm->getClassNameFromDiscriminatorValue($mapping, $reference); + $mongoId = $reference[$cmd . 'id']; + } + $id = $this->dm->getClassMetadata($className)->getPHPIdentifierValue($mongoId); + if (!$id) { + continue; + } + + // create a reference to the class and id + $reference = $this->dm->getReference($className, $id); + + // no custom sort so add the references right now in the order they are embedded + if ( ! $sorted) { + if ($mapping['strategy'] === 'set') { + $collection->set($key, $reference); + } else { + $collection->add($reference); + } + } + + // only query for the referenced object if it is not already initialized or the collection is sorted + if (($reference instanceof Proxy && ! $reference->__isInitialized__) || $sorted) { + $groupedIds[$className][$id] = $mongoId; + } + } + foreach ($groupedIds as $className => $ids) { + $class = $this->dm->getClassMetadata($className); + $mongoCollection = $this->dm->getDocumentCollection($className); + $criteria = array_merge( + array('_id' => array($cmd . 'in' => $ids)), + $this->dm->getFilterCollection()->getFilterCriteria($class), + isset($mapping['criteria']) ? $mapping['criteria'] : array() + ); + $cursor = $mongoCollection->find($criteria); + if (isset($mapping['sort'])) { + $cursor->sort($mapping['sort']); + } + if (isset($mapping['limit'])) { + $cursor->limit($mapping['limit']); + } + if (isset($mapping['skip'])) { + $cursor->skip($mapping['skip']); + } + if (isset($hints[Query::HINT_SLAVE_OKAY])) { + $cursor->slaveOkay(true); + } + $documents = $cursor->toArray(); + foreach ($documents as $documentData) { + $document = $this->uow->getById((string) $documentData['_id'], $class->rootDocumentName); + $data = $this->hydratorFactory->hydrate($document, $documentData); + $this->uow->setOriginalDocumentData($document, $data); + $document->__isInitialized__ = true; + if ($sorted) { + $collection->add($document); + } + } + } + } + + private function loadReferenceManyCollectionInverseSide(PersistentCollection $collection) + { + $hints = $collection->getHints(); + $mapping = $collection->getMapping(); + $owner = $collection->getOwner(); + $ownerClass = $this->dm->getClassMetadata(get_class($owner)); + $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); + $mappedByMapping = $targetClass->fieldMappings[$mapping['mappedBy']]; + $mappedByFieldName = isset($mappedByMapping['simple']) && $mappedByMapping['simple'] ? $mapping['mappedBy'] : $mapping['mappedBy'].'.$id'; + $criteria = array_merge( + array($mappedByFieldName => $ownerClass->getIdentifierObject($owner)), + isset($mapping['criteria']) ? $mapping['criteria'] : array() + ); + $qb = $this->dm->createQueryBuilder($mapping['targetDocument']) + ->setQueryArray($criteria); + + if (isset($mapping['sort'])) { + $qb->sort($mapping['sort']); + } + if (isset($mapping['limit'])) { + $qb->limit($mapping['limit']); + } + if (isset($mapping['skip'])) { + $qb->skip($mapping['skip']); + } + if (isset($hints[Query::HINT_SLAVE_OKAY])) { + $qb->slaveOkay(true); + } + $documents = $qb->getQuery()->execute()->toArray(); + foreach ($documents as $key => $document) { + if ($mapping['strategy'] === 'set') { + $collection->set($key, $document); + } else { + $collection->add($document); + } + } + } + + private function loadReferenceManyWithRepositoryMethod(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $cursor = $this->dm->getRepository($mapping['targetDocument'])->$mapping['repositoryMethod']($collection->getOwner()); + if (isset($mapping['sort']) && $mapping['sort']) { + $cursor->sort($mapping['sort']); + } + if (isset($mapping['limit']) && $mapping['limit']) { + $cursor->limit($mapping['limit']); + } + if (isset($mapping['skip']) && $mapping['skip']) { + $cursor->skip($mapping['skip']); + } + if (isset($hints[Query::HINT_SLAVE_OKAY])) { + $cursor->slaveOkay(true); + } + $documents = $cursor->toArray(); + foreach ($documents as $document) { + $collection->add($document); + } + } + + /** + * Prepares a sort array and converts PHP property names to MongoDB field names. + * + * @param array $sort + * @return array $newSort + */ + public function prepareSort($sort) + { + $metadata = null; + $newSort = array(); + foreach ($sort as $key => $value) { + $key = $this->prepareFieldName($key); + $newSort[$key] = $value; + } + return $newSort; + } + + /** + * Prepare a mongodb field name and convert the PHP property names to MongoDB field names. + * + * @param string $key + * @return string $preparedKey + */ + public function prepareFieldName($key) + { + $this->prepareQueryElement($key, null, null, false); + return $key; + } + + /** + * Prepares a query array by converting the portable Doctrine types to the types mongodb expects. + * + * @param string|array $query + * @return array $newQuery + */ + public function prepareQuery($query = array()) + { + if (is_scalar($query) || $query instanceof \MongoId) { + $query = array('_id' => $query); + } + if ($query === null) { + $query = array(); + } + $query = array_merge($query, $this->dm->getFilterCollection()->getFilterCriteria($this->class)); + + if ($this->class->hasDiscriminator() && ! isset($query[$this->class->discriminatorField['name']])) { + $discriminatorValues = $this->getClassDiscriminatorValues($this->class); + $query[$this->class->discriminatorField['name']] = array('$in' => $discriminatorValues); + } + $newQuery = array(); + if ($query) { + foreach ($query as $key => $value) { + if (isset($key[0]) && $key[0] === $this->cmd && is_array($value)) { + $newQuery[$key] = $this->prepareSubQuery($value); + } else { + $newQuery[$key] = $this->prepareQueryElement($key, $value, null, true); + } + } + $newQuery = $this->convertTypes($newQuery); + } + return $newQuery; + } + + /** + * Prepares a new object array by converting the portable Doctrine types to the types mongodb expects. + * + * @param string|array newObj + * @return array $newQuery + */ + public function prepareNewObj($newObj) + { + $prepared = array(); + if ($newObj) { + foreach ($newObj as $key => $value) { + if (isset($key[0]) && $key[0] === $this->cmd && is_array($value)) { + $prepared[$key] = $this->prepareSubQuery($value); + } else { + $prepared[$key] = $this->prepareQueryElement($key, $value, null, true); + } + } + $prepared = $this->convertTypes($prepared); + } + return $prepared; + } + + /** + * Convert a subquery. + * + * @see prepareQuery() + * @param array $query The query to convert. + * @return array $newQuery The converted query. + */ + private function prepareSubQuery($query) + { + $newQuery = array(); + if ($query) { + foreach ($query as $key => $value) { + if (isset($key[0]) && $key[0] === $this->cmd && is_array($value)) { + $newQuery[$key] = $this->prepareSubQuery($value); + } else { + $newQuery[$key] = $this->prepareQueryElement($key, $value, null, true); + } + } + $newQuery = $this->convertTypes($newQuery); + } + return $newQuery; + } + + /** + * Converts any local PHP variable types to their related MongoDB type. + * + * @param array $query + * @return array $query + */ + private function convertTypes(array $query) + { + foreach ($query as $key => $value) { + if (is_array($value)) { + $query[$key] = $this->convertTypes($value); + } else { + $query[$key] = Type::convertPHPToDatabaseValue($value); + } + } + return $query; + } + + /** + * Prepares a query value and converts the php value to the database value if it is an identifier. + * It also handles converting $fieldName to the database name if they are different. + * + * @param string $fieldName + * @param string $value + * @param ClassMetadata $metadata Defaults to $this->class + * @param bool $prepareValue Whether or not to prepare the value + * @return mixed $value + */ + private function prepareQueryElement(&$fieldName, $value = null, $metadata = null, $prepareValue = true) + { + $metadata = ($metadata === null) ? $this->class : $metadata; + + // Process "association.fieldName" + if (strpos($fieldName, '.') !== false) { + $e = explode('.', $fieldName); + + if (!isset($metadata->fieldMappings[$e[0]])) { + return $value; + } + + $mapping = $metadata->fieldMappings[$e[0]]; + $e[0] = $mapping['name']; + $fieldName = $e[0] . '.' .$e[1]; + if ($e[1] != '$') { + $fieldName = $e[0] . '.' .$e[1]; + $objectProperty = $e[1]; + $objectPropertyPrefix = ''; + $fieldHasCollectionItemPointer = false; + } else { + $fieldName = $e[0] . '.' .$e[1] . '.' .$e[2]; + $objectProperty = $e[2]; + $objectPropertyPrefix = $e[1] . '.'; + $fieldHasCollectionItemPointer = true; + } + + if (isset($mapping['targetDocument'])) { + $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); + if ($targetClass->hasField($objectProperty)) { + if ($targetClass->identifier === $objectProperty) { + $targetMapping = $targetClass->getFieldMapping($objectProperty); + $objectProperty = $targetMapping['name']; + if (isset($mapping['reference']) && $mapping['reference']) { + $fieldName = $mapping['simple'] ? $e[0] . '.' .$objectPropertyPrefix . $objectProperty : $e[0] . '.$id'; + } else { + $fieldName = $e[0] . '.' .$objectPropertyPrefix . $objectProperty; + } + if ($prepareValue === true) { + if (is_array($value)) { + $keys = array_keys($value); + if (isset($keys[0][0]) && $keys[0][0] === $this->cmd) { + foreach ($value[$keys[0]] as $k => $v) { + $value[$keys[0]][$k] = $targetClass->getDatabaseIdentifierValue($v); + } + } else { + foreach ($value as $k => $v) { + $value[$k] = $targetClass->getDatabaseIdentifierValue($v); + } + } + } else { + $value = $targetClass->getDatabaseIdentifierValue($value); + } + } + + } else { + $targetMapping = $targetClass->getFieldMapping($objectProperty); + $objectProperty = $targetMapping['name']; + $fieldName = $e[0] . '.' . $objectPropertyPrefix . $objectProperty; + + if (count($e) > 2 + $fieldHasCollectionItemPointer ? 1 : 0) { + if ($fieldHasCollectionItemPointer) { + unset($e[2]); + } + unset($e[0], $e[1]); + $key = implode('.', $e); + + if (isset($targetMapping['targetDocument'])) { + $nextTargetClass = $this->dm->getClassMetadata($targetMapping['targetDocument']); + $value = $this->prepareQueryElement($key, $value, $nextTargetClass, $prepareValue); + } else { + $value = $this->prepareQueryElement($key, $value, null, $prepareValue); + } + + $fieldName .= '.' . $key; + } + } + } + } elseif ($mapping['type'] === 'hash') { + $fieldName = implode('.', $e); + } + + // Process all non identifier fields + // We only change the field names here to the mongodb field name used for persistence + } elseif ($metadata->hasField($fieldName) && ! $metadata->isIdentifier($fieldName)) { + $mapping = $metadata->fieldMappings[$fieldName]; + $fieldName = $mapping['name']; + + if ($prepareValue === true && isset($mapping['reference']) && isset($mapping['simple']) && $mapping['simple']) { + if (is_array($value)) { + $value = $this->prepareSubQuery($value); + } else { + $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); + $value = $targetClass->getDatabaseIdentifierValue($value); + } + } + + // Process identifier + } elseif (($metadata->hasField($fieldName) && $metadata->isIdentifier($fieldName)) || $fieldName === '_id') { + $fieldName = '_id'; + if ($prepareValue === true) { + if (is_array($value)) { + foreach ($value as $k => $v) { + if ($k[0] === '$' && is_array($v)) { + foreach ($v as $k2 => $v2) { + $value[$k][$k2] = $metadata->getDatabaseIdentifierValue($v2); + } + } else { + $value[$k] = $metadata->getDatabaseIdentifierValue($v); + } + } + } else { + $value = $metadata->getDatabaseIdentifierValue($value); + } + } + } + return $value; + } + + /** + * Gets the array of discriminator values for the given ClassMetadata + * + * @param ClassMetadata $metadata + * @return array + */ + private function getClassDiscriminatorValues(ClassMetadata $metadata) + { + $discriminatorValues = array($metadata->discriminatorValue); + foreach ($metadata->subClasses as $className) { + if ($key = array_search($className, $metadata->discriminatorMap)) { + $discriminatorValues[] = $key; + } + } + return $discriminatorValues; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php new file mode 100644 index 00000000..10c6d590 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php @@ -0,0 +1,421 @@ +. + */ +namespace Doctrine\ODM\MongoDB\Persisters; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\Types\Type; + +/** + * PersistenceBuilder builds the queries used by the persisters to update and insert + * documents when a DocumentManager is flushed. It uses the changeset information in the + * UnitOfWork to build queries using atomic operators like $set, $unset, etc. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class PersistenceBuilder +{ + /** + * The DocumentManager instance. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The UnitOfWork instance. + * + * @var Doctrine\ODM\MongoDB\UnitOfWork + */ + private $uow; + + /** + * Initializes a new PersistenceBuilder instance. + * + * @param Doctrine\ODM\MongoDB\DocumentManager $dm + * @param Doctrine\ODM\MongoDB\UnitOfWork $uow + */ + public function __construct(DocumentManager $dm, UnitOfWork $uow, $cmd) + { + $this->dm = $dm; + $this->uow = $uow; + $this->cmd = $cmd; + } + + /** + * Prepares the array that is ready to be inserted to mongodb for a given object document. + * + * @param object $document + * @return array $insertData + */ + public function prepareInsertData($document) + { + $class = $this->dm->getClassMetadata(get_class($document)); + $changeset = $this->uow->getDocumentChangeSet($document); + + $insertData = array(); + foreach ($class->fieldMappings as $mapping) { + + // many collections are inserted later + if ($mapping['type'] === ClassMetadata::MANY) { + continue; + } + + $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null; + + // Generate a document identifier + if ($new === null && $class->identifier === $mapping['fieldName'] && $class->generatorType !== ClassMetadata::GENERATOR_TYPE_NONE) { + $new = $class->idGenerator->generate($this->dm, $document); + } + + // Don't store null values unless nullable === true + if ($new === null && $mapping['nullable'] === false) { + continue; + } + + $value = null; + if ($new !== null) { + // @Field, @String, @Date, etc. + if ( ! isset($mapping['association'])) { + $value = Type::getType($mapping['type'])->convertToDatabaseValue($new); + + // @ReferenceOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) { + if ($mapping['isInverseSide']) { + continue; + } + + $oid = spl_object_hash($new); + + if ($this->isScheduledForInsert($new)) { + // The associated document $new is not yet persisted, so we must + // set $new = null, in order to insert a null value and schedule an + // extra update on the UnitOfWork. + $this->uow->scheduleExtraUpdate($document, array( + $mapping['fieldName'] => array(null, $new) + )); + } else { + $value = $this->prepareReferencedDocumentValue($mapping, $new); + } + + // @EmbedOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { + $value = $this->prepareEmbeddedDocumentValue($mapping, $new); + + // @ReferenceMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { + $value = array(); + foreach ($new as $reference) { + $value[] = $this->prepareReferencedDocumentValue($mapping, $reference); + } + + // @EmbedMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_MANY) { + $value = array(); + foreach ($new as $reference) { + $value[] = $this->prepareEmbeddedDocumentValue($mapping, $reference); + } + } + } + + $insertData[$mapping['name']] = $value; + } + + // add discriminator if the class has one + if ($class->hasDiscriminator()) { + $insertData[$class->discriminatorField['name']] = $class->discriminatorValue; + } + + return $insertData; + } + + /** + * Prepares the update query to update a given document object in mongodb. + * + * @param object $document + * @return array $updateData + */ + public function prepareUpdateData($document) + { + $oid = spl_object_hash($document); + $class = $this->dm->getClassMetadata(get_class($document)); + $changeset = $this->uow->getDocumentChangeSet($document); + + $updateData = array(); + foreach ($changeset as $fieldName => $change) { + $mapping = $class->fieldMappings[$fieldName]; + + // skip identifiers + if (isset($mapping['id']) && $mapping['id'] === true) { + continue; + } + + list($old, $new) = $change; + + // @Inc + if ($mapping['type'] === 'increment') { + if ($new >= $old) { + $updateData[$this->cmd . 'inc'][$mapping['name']] = $new - $old; + } else { + $updateData[$this->cmd . 'inc'][$mapping['name']] = ($old - $new) * -1; + } + + // @Field, @String, @Date, etc. + } elseif ( ! isset($mapping['association'])) { + if (isset($new) || $mapping['nullable'] === true) { + $updateData[$this->cmd . 'set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new)); + } else { + $updateData[$this->cmd . 'unset'][$mapping['name']] = true; + } + + // @EmbedOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { + // If we have a new embedded document then lets set the whole thing + if ($new && $this->uow->isScheduledForInsert($new)) { + $updateData[$this->cmd . 'set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new); + + // If we don't have a new value then lets unset the embedded document + } elseif ( ! $new) { + $updateData[$this->cmd . 'unset'][$mapping['name']] = true; + + // Update existing embedded document + } else { + $update = $this->prepareUpdateData($new); + foreach ($update as $cmd => $values) { + foreach ($values as $key => $value) { + $updateData[$cmd][$mapping['name'] . '.' . $key] = $value; + } + } + } + + // @EmbedMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_MANY) { + foreach ($new as $key => $embeddedDoc) { + if ( ! $this->uow->isScheduledForInsert($embeddedDoc)) { + $update = $this->prepareUpdateData($embeddedDoc); + foreach ($update as $cmd => $values) { + foreach ($values as $name => $value) { + $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value; + } + } + } + } + + // @ReferenceOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { + if (isset($new) || $mapping['nullable'] === true) { + $updateData[$this->cmd . 'set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new)); + } else { + $updateData[$this->cmd . 'unset'][$mapping['name']] = true; + } + + // @ReferenceMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { + // Do nothing right now + } + } + return $updateData; + } + + /** + * Prepares the update query to upsert a given document object in mongodb. + * + * @param object $document + * @return array $updateData + */ + public function prepareUpsertData($document) + { + $oid = spl_object_hash($document); + $class = $this->dm->getClassMetadata(get_class($document)); + $changeset = $this->uow->getDocumentChangeSet($document); + + $updateData = array(); + foreach ($changeset as $fieldName => $change) { + $mapping = $class->fieldMappings[$fieldName]; + + list($old, $new) = $change; + + // @Inc + if ($mapping['type'] === 'increment') { + if ($new >= $old) { + $updateData[$this->cmd . 'inc'][$mapping['name']] = $new - $old; + } else { + $updateData[$this->cmd . 'inc'][$mapping['name']] = ($old - $new) * -1; + } + + // @Field, @String, @Date, etc. + } elseif ( ! isset($mapping['association'])) { + if (isset($new) || $mapping['nullable'] === true) { + $updateData[$this->cmd . 'set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new)); + } + + // @EmbedOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { + // If we have a new embedded document then lets set the whole thing + if ($new && $this->uow->isScheduledForInsert($new)) { + $updateData[$this->cmd . 'set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new); + + // If we don't have a new value then do nothing on upsert + } elseif ( ! $new) { + + // Update existing embedded document + } else { + $update = $this->prepareUpsertData($new); + foreach ($update as $cmd => $values) { + foreach ($values as $key => $value) { + $updateData[$cmd][$mapping['name'] . '.' . $key] = $value; + } + } + } + + // @EmbedMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_MANY) { + foreach ($new as $key => $embeddedDoc) { + if ( ! $this->uow->isScheduledForInsert($embeddedDoc)) { + $update = $this->prepareUpsertData($embeddedDoc); + foreach ($update as $cmd => $values) { + foreach ($values as $name => $value) { + $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value; + } + } + } + } + + // @ReferenceOne + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { + if (isset($new) || $mapping['nullable'] === true) { + $updateData[$this->cmd . 'set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new)); + } + + // @ReferenceMany + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { + // Do nothing right now + } + } + + // add discriminator if the class has one + if ($class->hasDiscriminator()) { + $updateData[$this->cmd . 'set'][$class->discriminatorField['name']] = $class->discriminatorValue; + } + + return $updateData; + } + + /** + * Returns the reference representation to be stored in mongodb or null if not applicable. + * + * @param array $referenceMapping + * @param object $document + * @return array $referenceDocumentValue + */ + public function prepareReferencedDocumentValue(array $referenceMapping, $document) + { + return $this->dm->createDBRef($document, $referenceMapping); + } + + /** + * Prepares array of values to be stored in mongo to represent embedded object. + * + * @param array $embeddedMapping + * @param object $embeddedDocument + * @return array|object $embeddedDocumentValue + */ + public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument) + { + $className = get_class($embeddedDocument); + $class = $this->dm->getClassMetadata($className); + $embeddedDocumentValue = array(); + foreach ($class->fieldMappings as $mapping) { + // Skip not saved fields + if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { + continue; + } + + $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument); + + // Generate a document identifier + if ($rawValue === null && $class->identifier === $mapping['fieldName'] && $class->generatorType !== ClassMetadata::GENERATOR_TYPE_NONE) { + $rawValue = $class->idGenerator->generate($this->dm, $embeddedDocument); + $class->setIdentifierValue($embeddedDocument, $rawValue); + } + + $value = null; + if ($rawValue !== null) { + /** @Field, @String, @Date, etc. */ + if ( ! isset($mapping['association'])) { + $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue); + + /** @EmbedOne */ + } elseif (isset($mapping['association']) && $mapping['association'] == ClassMetadata::EMBED_ONE) { + $value = $this->prepareEmbeddedDocumentValue($mapping, $rawValue); + + /** @EmbedMany */ + } elseif (isset($mapping['association']) && $mapping['association'] == ClassMetadata::EMBED_MANY) { + if ($mapping['strategy'] !== 'set') { + foreach ($rawValue as $key => $item) { + $value[$key] = $this->prepareEmbeddedDocumentValue($mapping, $item); + } + } + + /** @ReferenceOne */ + } elseif (isset($mapping['association']) && $mapping['association'] == ClassMetadata::REFERENCE_ONE) { + $value = $this->prepareReferencedDocumentValue($mapping, $rawValue); + + /** @ReferenceMany */ + } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { + $value = array(); + foreach ($rawValue as $reference) { + $value[] = $this->prepareReferencedDocumentValue($mapping, $reference); + } + } + } + + if ($value === null && $mapping['nullable'] === false) { + continue; + } + $embeddedDocumentValue[$mapping['name']] = $value; + } + + // Store a discriminator value if the embedded document is not mapped explicitly to a targetDocument + if ( ! isset($embeddedMapping['targetDocument'])) { + $discriminatorField = isset($embeddedMapping['discriminatorField']) ? $embeddedMapping['discriminatorField'] : '_doctrine_class_name'; + $discriminatorValue = isset($embeddedMapping['discriminatorMap']) ? array_search($class->getName(), $embeddedMapping['discriminatorMap']) : $class->getName(); + $embeddedDocumentValue[$discriminatorField] = $discriminatorValue; + } + + if ($class->hasDiscriminator()) { + $embeddedDocumentValue[$class->discriminatorField['name']] = $class->discriminatorValue; + } + + // Fix so that we can force empty embedded document to store itself as a hash instead of an array + if (empty($embeddedDocumentValue)) { + return (object) $embeddedDocumentValue; + } + + return $embeddedDocumentValue; + } + + private function isScheduledForInsert($document) + { + return $this->uow->isScheduledForInsert($document) + || $this->uow->getDocumentPersister(get_class($document))->isQueuedForInsert($document); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/Proxy.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/Proxy.php new file mode 100644 index 00000000..9c82f512 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/Proxy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Proxy; + +use Doctrine\Common\Persistence\Proxy as BaseProxy; + +/** + * Document Proxy interface. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +interface Proxy extends BaseProxy {} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyException.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyException.php new file mode 100644 index 00000000..517d50cd --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyException.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Proxy; + +use Doctrine\ODM\MongoDB\MongoDBException; + +/** + * MongoDB ODM Proxy Exception + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +class ProxyException extends MongoDBException +{ + public static function proxyDirectoryRequired() + { + return new self("You must configure a proxy directory. See docs for details"); + } + + public static function proxyDirectoryNotWritable() + { + return new self("Your proxy directory must be writable."); + } + + public static function proxyNamespaceRequired() + { + return new self("You must configure a proxy namespace. See docs for details"); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php new file mode 100644 index 00000000..04fb2a4c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php @@ -0,0 +1,412 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Proxy; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * This factory is used to create proxy objects for documents at runtime. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +class ProxyFactory +{ + /** + * Marker for Proxy class names. + * + * @var string + */ + const MARKER = '__CG__'; + + /** The DocumentManager this factory is bound to. */ + private $dm; + /** Whether to automatically (re)generate proxy classes. */ + private $autoGenerate; + /** The namespace that contains all proxy classes. */ + private $proxyNamespace; + /** The directory that contains all proxy classes. */ + private $proxyDir; + + /** + * Used to match very simple id methods that don't need + * to be proxied since the identifier is known. + * + * @var string + */ + const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given DocumentManager. + * + * @param DocumentManager $dm The DocumentManager the new factory works for. + * @param string $proxyDir The directory to use for the proxy classes. It must exist. + * @param string $proxyNs The namespace to use for the proxy classes. + * @param boolean $autoGenerate Whether to automatically generate proxy classes. + */ + public function __construct(DocumentManager $dm, $proxyDir, $proxyNs, $autoGenerate = false) + { + if ( ! $proxyDir) { + throw ProxyException::proxyDirectoryRequired(); + } + if ( ! $proxyNs) { + throw ProxyException::proxyNamespaceRequired(); + } + $this->dm = $dm; + $this->proxyDir = $proxyDir; + $this->autoGenerate = $autoGenerate; + $this->proxyNamespace = $proxyNs; + } + + /** + * Gets a reference proxy instance for the document of the given type and identified by + * the given identifier. + * + * @param string $className + * @param mixed $identifier + * @return object + */ + public function getProxy($className, $identifier) + { + $fqn = self::generateProxyClassName($className, $this->proxyNamespace); + + if (! class_exists($fqn, false)) { + $fileName = $this->getProxyFileName($className); + if ($this->autoGenerate) { + $this->generateProxyClass($this->dm->getClassMetadata($className), $fileName, self::$proxyClassTemplate); + } + require $fileName; + } + + if ( ! $this->dm->getMetadataFactory()->hasMetadataFor($fqn)) { + $this->dm->getMetadataFactory()->setMetadataFor($fqn, $this->dm->getClassMetadata($className)); + } + + $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($className); + + return new $fqn($documentPersister, $identifier); + } + + /** + * Generate the Proxy file name + * + * @param string $className + * @return string + */ + private function getProxyFileName($className) + { + return $this->proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php'; + } + + /** + * Generates proxy classes for all given classes. + * + * @param array $classes The classes (ClassMetadata instances) for which to generate proxies. + * @param string $toDir The target directory of the proxy classes. If not specified, the + * directory configured on the Configuration of the DocumentManager used + * by this factory is used. + */ + public function generateProxyClasses(array $classes, $toDir = null) + { + $proxyDir = $toDir ?: $this->proxyDir; + $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + foreach ($classes as $class) { + /* @var $class ClassMetadata */ + if ($class->isMappedSuperclass) { + continue; + } + + $proxyFileName = $this->getProxyFileName($class->name); + $this->generateProxyClass($class, $proxyFileName, self::$proxyClassTemplate); + } + } + + /** + * Generates a proxy class file. + * + * @param $class + * @param $proxyClassName + * @param $file The path of the file to write to. + */ + private function generateProxyClass($class, $fileName, $file) + { + $methods = $this->generateMethods($class); + $sleepImpl = $this->generateSleep($class); + $cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive + + $placeholders = array( + '', + '', '', + '', '', '' + ); + + $className = ltrim($class->name, '\\'); + $proxyClassName = self::generateProxyClassName($class->name, $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + $proxyClassNamespace = strrev($parts[1]); + $proxyClassName = strrev($parts[0]); + + $replacements = array( + $proxyClassNamespace, + $proxyClassName, + $className, + $methods, + $sleepImpl, + $cloneImpl + ); + + $file = str_replace($placeholders, $replacements, $file); + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory)) { + if (false === @mkdir($parentDirectory, 0775, true)) { + throw ProxyException::proxyDirectoryNotWritable(); + } + } else if ( ! is_writable($parentDirectory)) { + throw ProxyException::proxyDirectoryNotWritable(); + } + + file_put_contents($fileName, $file, LOCK_EX); + } + + /** + * Generates the methods of a proxy class. + * + * @param ClassMetadata $class + * @return string The code of the generated methods. + */ + private function generateMethods(ClassMetadata $class) + { + $methods = ''; + + $methodNames = array(); + foreach ($class->reflClass->getMethods() as $method) { + /* @var $method ReflectionMethod */ + if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) { + continue; + } + $methodNames[$method->getName()] = true; + + if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) { + $methods .= "\n" . ' public function '; + if ($method->returnsReference()) { + $methods .= '&'; + } + $methods .= $method->getName() . '('; + $firstParam = true; + $parameterString = $argumentString = ''; + + foreach ($method->getParameters() as $param) { + if ($firstParam) { + $firstParam = false; + } else { + $parameterString .= ', '; + $argumentString .= ', '; + } + + // We need to pick the type hint class too + if (($paramClass = $param->getClass()) !== null) { + $parameterString .= '\\' . $paramClass->getName() . ' '; + } else if ($param->isArray()) { + $parameterString .= 'array '; + } + + if ($param->isPassedByReference()) { + $parameterString .= '&'; + } + + $parameterString .= '$' . $param->getName(); + $argumentString .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterString .= ' = ' . var_export($param->getDefaultValue(), true); + } + } + + $methods .= $parameterString . ')'; + $methods .= "\n" . ' {' . "\n"; + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($method->getName(), 3)); + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return $this->__identifier__;' . "\n"; + $methods .= ' }' . "\n"; + } + $methods .= ' $this->__load();' . "\n"; + $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; + $methods .= "\n" . ' }' . "\n"; + } + } + + return $methods; + } + + /** + * Check 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 document, but do not display anything else). + * + * @param ReflectionMethod $method + * @param ClassMetadata $class + * @return bool + */ + private function isShortIdentifierGetter($method, $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 && + substr($method->getName(), 0, 3) == "get" && + $class->identifier === $identifier && + $class->hasField($identifier) && + (($method->getEndLine() - $method->getStartLine()) <= 4) + && in_array($class->fieldMappings[$identifier]['type'], array('id', 'custom_id')) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + return false; + } + + /** + * Generates the code for the __sleep method for a proxy class. + * + * @param $class + * @return string + */ + private function generateSleep(ClassMetadata $class) + { + $sleepImpl = ''; + + if ($class->reflClass->hasMethod('__sleep')) { + $sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());"; + } else { + $sleepImpl .= "return array('__isInitialized__', "; + $first = true; + + foreach ($class->getReflectionProperties() as $name => $prop) { + if ($first) { + $first = false; + } else { + $sleepImpl .= ', '; + } + + $sleepImpl .= "'" . $name . "'"; + } + + $sleepImpl .= ');'; + } + + return $sleepImpl; + } + + /** Proxy class code template */ + private static $proxyClassTemplate = +'; + +use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; + +/** + * THIS CLASS WAS GENERATED BY THE DOCTRINE ODM. DO NOT EDIT THIS FILE. + */ +class extends \ implements \Doctrine\ODM\MongoDB\Proxy\Proxy +{ + private $__documentPersister__; + public $__identifier__; + public $__isInitialized__ = false; + public function __construct(DocumentPersister $documentPersister, $identifier) + { + $this->__documentPersister__ = $documentPersister; + $this->__identifier__ = $identifier; + } + /** @private */ + public function __load() + { + if (!$this->__isInitialized__ && $this->__documentPersister__) { + $this->__isInitialized__ = true; + + if (method_exists($this, "__wakeup")) { + // call this after __isInitialized__to avoid infinite recursion + // but before loading to emulate what ClassMetadata::newInstance() + // provides. + $this->__wakeup(); + } + + if ($this->__documentPersister__->load($this->__identifier__, $this) === null) { + throw \Doctrine\ODM\MongoDB\DocumentNotFoundException::documentNotFound(get_class($this), $this->__identifier__); + } + unset($this->__documentPersister__, $this->__identifier__); + } + } + + /** @private */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + + + public function __sleep() + { + + } + + public function __clone() + { + if (!$this->__isInitialized__ && $this->__documentPersister__) { + $this->__isInitialized__ = true; + $class = $this->__documentPersister__->getClassMetadata(); + $original = $this->__documentPersister__->load($this->__identifier__); + if ($original === null) { + throw \Doctrine\ODM\MongoDB\MongoDBException::documentNotFound(get_class($this), $this->__identifier__); + } + foreach ($class->reflFields AS $field => $reflProperty) { + $reflProperty->setValue($this, $reflProperty->getValue($original)); + } + unset($this->__documentPersister__, $this->__identifier__); + } + + } +}'; + + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\'.self::MARKER.'\\' . ltrim($className, '\\'); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Builder.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Builder.php new file mode 100644 index 00000000..8926ea31 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Builder.php @@ -0,0 +1,312 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Hydrator; +use Doctrine\ODM\MongoDB\Query\Expr; +use Doctrine\ODM\MongoDB\UnitOfWork; + +/** + * Query builder for ODM. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class Builder extends \Doctrine\MongoDB\Query\Builder +{ + /** + * The DocumentManager instance for this query + * + * @var DocumentManager + */ + private $dm; + + /** + * The ClassMetadata instance. + * + * @var ClassMetadata + */ + private $class; + + /** + * Whether or not to hydrate the data to documents. + * + * @var boolean + */ + private $hydrate = true; + + /** + * Whether or not to refresh the data for documents that are already in the identity map. + * + * @var boolean + */ + private $refresh = false; + + /** + * Array of primer Closure instances. + * + * @var array + */ + private $primers = array(); + + /** + * Whether or not to require indexes. + * + * @var bool + */ + private $requireIndexes; + + public function __construct(DocumentManager $dm, $cmd, $documentName = null) + { + $this->dm = $dm; + $this->expr = new Expr($dm, $cmd); + $this->cmd = $cmd; + if ($documentName !== null) { + $this->setDocumentName($documentName); + } + } + + /** + * Set whether or not to require indexes. + * + * @param bool $requireIndexes + * @return Builder + */ + public function requireIndexes($requireIndexes = true) + { + $this->requireIndexes = $requireIndexes; + return $this; + } + + /** + * Use a primer to load the current fields referenced data efficiently. + * + * $qb->field('user')->prime(true); + * $qb->field('user')->prime(function(DocumentManager $dm) { + * // do something that will prime all the associated users in one query + * }); + * + * @param Closure|boolean $primer + * @return Builder + */ + public function prime($primer = true) + { + $this->primers[$this->currentField] = $primer; + return $this; + } + + /** + * @param bool $bool + * @return Builder + */ + public function hydrate($bool = true) + { + $this->hydrate = $bool; + return $this; + } + + /** + * @param bool $bool + * @return Builder + */ + public function refresh($bool = true) + { + $this->refresh = $bool; + return $this; + } + + /** + * Change the query type to find and optionally set and change the class being queried. + * + * @param string $documentName + * @return Builder + */ + public function find($documentName = null) + { + $this->setDocumentName($documentName); + return parent::find(); + } + + /** + * @param string $documentName + * @return Builder + */ + public function findAndUpdate($documentName = null) + { + $this->setDocumentName($documentName); + return parent::findAndUpdate(); + } + + public function returnNew($bool = true) + { + $this->refresh(true); + return parent::returnNew($bool); + } + + /** + * @param string $documentName + * @return Builder + */ + public function findAndRemove($documentName = null) + { + $this->setDocumentName($documentName); + return parent::findAndRemove(); + } + + /** + * @param string $documentName + * @return Builder + */ + public function update($documentName = null) + { + $this->setDocumentName($documentName); + return parent::update(); + } + + /** + * @param string $documentName + * @return Builder + */ + public function insert($documentName = null) + { + $this->setDocumentName($documentName); + return parent::insert(); + } + + /** + * @param string $documentName + * @return Builder + */ + public function remove($documentName = null) + { + $this->setDocumentName($documentName); + return parent::remove(); + } + + /** + * @param $document + * @return Builder + */ + public function references($document) + { + $this->expr->references($document); + return $this; + } + + /** + * @param $document + * @return Builder + */ + public function includesReferenceTo($document) + { + $this->expr->includesReferenceTo($document); + return $this; + } + + /** + * Gets the Query executable. + * + * @param array $options + * @return QueryInterface $query + */ + public function getQuery(array $options = array()) + { + if ($this->query['type'] === Query::TYPE_MAP_REDUCE) { + $this->hydrate = false; + } + + $query = $this->query; + + $query['query'] = $this->expr->getQuery(); + $query['newObj'] = $this->expr->getNewObj(); + $query['sort'] = $this->dm->getUnitOfWork() + ->getDocumentPersister($this->class->name) + ->prepareSort($query['sort']); + + if ($this->class->slaveOkay) { + $query['slaveOkay'] = $this->class->slaveOkay; + } + + return new Query( + $this->dm, + $this->class, + $this->database, + $this->collection, + $query, + $options, + $this->cmd, + $this->hydrate, + $this->refresh, + $this->primers, + $this->requireIndexes + ); + } + + /** + * Create a new Query\Expr instance that can be used as an expression with the QueryBuilder + * + * @return Query\Expr $expr + */ + public function expr() + { + $expr = new Expr($this->dm, $this->cmd); + $expr->setClassMetadata($this->class); + + return $expr; + } + + private function setDocumentName($documentName) + { + if (is_array($documentName)) { + $documentNames = $documentName; + $documentName = $documentNames[0]; + + $discriminatorField = $this->dm->getClassMetadata($documentName)->discriminatorField['name']; + $discriminatorValues = $this->getDiscriminatorValues($documentNames); + $this->field($discriminatorField)->in($discriminatorValues); + } + + if ($documentName !== null) { + $this->collection = $this->dm->getDocumentCollection($documentName); + $this->database = $this->collection->getDatabase(); + $this->class = $this->dm->getClassMetadata($documentName); + + // Expr also needs to know + $this->expr->setClassMetadata($this->class); + } + } + + private function getDiscriminatorValues($classNames) + { + $discriminatorValues = array(); + $collections = array(); + foreach ($classNames as $className) { + $class = $this->dm->getClassMetadata($className); + $discriminatorValues[] = $class->discriminatorValue; + $key = $this->dm->getDocumentDatabase($className)->getName() . '.' . $class->getCollection(); + $collections[$key] = $key; + } + if (count($collections) > 1) { + throw new \InvalidArgumentException('Documents involved are not all mapped to the same database collection.'); + } + return $discriminatorValues; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Expr.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Expr.php new file mode 100644 index 00000000..b22b70cf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Expr.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; + +/** + * Query expression builder for ODM. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class Expr extends \Doctrine\MongoDB\Query\Expr +{ + /** + * The DocumentManager instance for this query + * + * @var DocumentManager + */ + private $dm; + + /** + * The ClassMetadata instance for the document being queried + * + * @var ClassMetadata + */ + private $class; + + public function __construct(DocumentManager $dm, $cmd) + { + $this->dm = $dm; + $this->cmd = $cmd; + } + + public function setClassMetadata(ClassMetadata $class) + { + $this->class = $class; + } + + /** + * Checks that the value of the current field is a reference to the supplied document. + */ + public function references($document) + { + if ($this->currentField) { + $mapping = $this->class->getFieldMapping($this->currentField); + $dbRef = $this->dm->createDBRef($document, $mapping); + + if (isset($mapping['simple']) && $mapping['simple']) { + $this->query[$mapping['name']] = $dbRef; + } else { + $keys = array('ref' => true, 'id' => true, 'db' => true); + + if (isset($mapping['targetDocument'])) { + unset($keys['ref'], $keys['db']); + } + + foreach ($keys as $key => $value) { + $this->query[$this->currentField . '.' . $this->cmd . $key] = $dbRef[$this->cmd . $key]; + } + } + } else { + $dbRef = $this->dm->createDBRef($document); + $this->query = $dbRef; + } + + return $this; + } + + /** + * Checks that the current field includes a reference to the supplied document. + */ + public function includesReferenceTo($document) + { + if ($this->currentField) { + $mapping = $this->class->getFieldMapping($this->currentField); + $dbRef = $this->dm->createDBRef($document, $mapping); + + if (isset($mapping['simple']) && $mapping['simple']) { + $this->query[$mapping['name']][$this->cmd . 'elemMatch'] = $dbRef; + } else { + $keys = array('ref' => true, 'id' => true, 'db' => true); + + if (isset($mapping['targetDocument'])) { + unset($keys['ref'], $keys['db']); + } + + foreach ($keys as $key => $value) { + $this->query[$this->currentField][$this->cmd . 'elemMatch'][$this->cmd . $key] = $dbRef[$this->cmd . $key]; + } + } + } else { + $dbRef = $this->dm->createDBRef($document); + $this->query[$this->cmd . 'elemMatch'] = $dbRef; + } + + return $this; + } + + public function getQuery() + { + return $this->dm->getUnitOfWork() + ->getDocumentPersister($this->class->name) + ->prepareQuery($this->query); + } + + public function getNewObj() + { + return $this->dm->getUnitOfWork() + ->getDocumentPersister($this->class->name) + ->prepareNewObj($this->newObj); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FieldExtractor.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FieldExtractor.php new file mode 100644 index 00000000..88fa6f28 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FieldExtractor.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query; + +/** + * Class responsible for extracting an array of field names that are involved in + * a given mongodb query. Used for checking if query is indexed. + * + * @see Doctrine\ODM\MongoDB\Query::isIndexed() + */ +class FieldExtractor +{ + private $query; + private $sort; + private $cmd; + + public function __construct(array $query, array $sort = array(), $cmd = '$') + { + $this->query = $query; + $this->sort = $sort; + $this->cmd = $cmd; + } + + public function getFields() + { + $fields = array(); + + foreach ($this->query as $k => $v) { + if (is_array($v) && isset($v[$this->cmd.'elemMatch'])) { + $elemMatchFields = $this->getFieldsFromElemMatch($v[$this->cmd.'elemMatch']); + foreach ($elemMatchFields as $field) { + $fields[] = $k.'.'.$field; + } + } else if ($this->isOperator($k, array('and', 'or'))) { + foreach ($v as $q) { + $test = new self($q); + $fields = array_merge($fields, $test->getFields()); + } + } else if ($k[0] !== $this->cmd) { + $fields[] = $k; + } + } + $fields = array_unique(array_merge($fields, array_keys($this->sort))); + return $fields; + } + + private function getFieldsFromElemMatch($elemMatch) + { + $fields = array(); + foreach ($elemMatch as $fieldName => $value) { + if ($this->isOperator($fieldName, 'where')) { + continue; + } + + if ($this->isOperator($fieldName, array('and', 'or'))) { + foreach ($value as $q) { + $test = new self($q); + $fields = array_merge($fields, $test->getFields()); + } + } else { + $fields[] = $fieldName; + } + } + return $fields; + } + + private function isOperator($fieldName, $operator) + { + if (!is_array($operator)) { + $operator = array($operator); + } + foreach ($operator as $op) { + if ($fieldName === $this->cmd.$op) { + return true; + } + } + return false; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Filter/BsonFilter.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Filter/BsonFilter.php new file mode 100644 index 00000000..eb6af370 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Filter/BsonFilter.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query\Filter; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetaData; + +/** + * The base class that user defined filters should extend. + * + * Handles the setting and escaping of parameters. + * + * @author Tim Roediger + */ +abstract class BsonFilter +{ + /** + * The entity manager. + * @var DocumentManager + */ + protected $dm; + + /** + * Parameters for the filter. + * @var array + */ + protected $parameters; + + /** + * Constructs the BsonFilter object. + * + * @param DocumentManager $dm The Document Manager + */ + final public function __construct(DocumentManager $dm) + { + $this->dm = $dm; + } + + /** + * Sets a parameter that can be used by the filter. + * + * @param string $name Name of the parameter. + * @param mixed $value Value of the parameter. + * + * @return BsonFilter The current Bson filter. + */ + final public function setParameter($name, $value) + { + $this->parameters[$name] = $value; + return $this; + } + + /** + * Gets a parameter to use in a query. + * + * These are not like SQL parameters. These parameters can hold anything, + * even objects. They are not automatically injected into a query, they + * are to be used in the addFilterCriteria method. + * + * @param string $name Name of the parameter. + * + * @return mixed The parameter. + */ + final public function getParameter($name) + { + if (!isset($this->parameters[$name])) { + throw new \InvalidArgumentException("Filter parameter '" . $name . "' is not set."); + } + return $this->parameters[$name]; + } + + /** + * Gets the criteria part to add to a query. + * + * @return array The criteria array, if there is available, empty array otherwise + */ + abstract public function addFilterCriteria(ClassMetadata $targetEntity); +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FilterCollection.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FilterCollection.php new file mode 100644 index 00000000..dd5f2598 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/FilterCollection.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query; + +use Doctrine\ODM\MongoDB\Configuration; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetaData; + +/** + * Collection class for all the query filters. + * + * @author Tim Roediger + */ +class FilterCollection +{ + /** + * The used Configuration. + * + * @var \Doctrine\ODM\MongoDB\Configuration + */ + private $config; + + /** + * The DocumentManager that "owns" this FilterCollection instance. + * + * @var \Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * Instances of enabled filters. + * + * @var array + */ + private $enabledFilters = array(); + + /** + * Constructor. + * + * @param DocumentManager $dm + */ + public function __construct(DocumentManager $dm) + { + $this->dm = $dm; + $this->config = $dm->getConfiguration(); + } + + /** + * Get all the enabled filters. + * + * @return array The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + * + * @return \Doctrine\ODM\MongoDB\Query\Filter\BsonFilter The enabled filter. + */ + public function enable($name) + { + if ( ! $this->has($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if ( ! $this->isEnabled($name)) { + $filterClass = $this->config->getFilterClassName($name); + $this->enabledFilters[$name] = new $filterClass($this->dm); + } + + return $this->enabledFilters[$name]; + } + + /** + * Disables a filter. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ODM\MongoDB\Query\Filter\BsonFilter The disabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function disable($name) + { + // Get the filter to return it + $filter = $this->getFilter($name); + + unset($this->enabledFilters[$name]); + + return $filter; + } + + /** + * Get an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ODM\MongoDB\Query\Filter\BsonFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if ( ! $this->isEnabled($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + return $this->enabledFilters[$name]; + } + + /** + * Checks whether filter with given name is defined. + * + * @param string $name Name of the filter. + * @return bool true if the filter exists, false if not. + */ + public function has($name) + { + return null !== $this->config->getFilterClassName($name); + } + + /** + * Checks whether filter with given name is enabled. + * + * @param string $name Name of the filter + * @return bool + */ + public function isEnabled($name) + { + return isset($this->enabledFilters[$name]); + } + + /** + * Gets enabled filter criteria. + * + * @param array $criteria + * @return array + */ + public function getFilterCriteria(ClassMetadata $metaData){ + $criteria = array(); + foreach ($this->enabledFilters as $filter) { + $criteria = array_merge($criteria, $filter->addFilterCriteria($metaData)); + } + return $criteria; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Query.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Query.php new file mode 100644 index 00000000..3a952dca --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Query/Query.php @@ -0,0 +1,294 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Query; + +use Doctrine\MongoDB\Cursor as BaseCursor; +use Doctrine\MongoDB\EagerCursor as BaseEagerCursor; +use Doctrine\MongoDB\LoggableCursor as BaseLoggableCursor; +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\MongoDBException; +use Doctrine\ODM\MongoDB\EagerCursor; +use Doctrine\ODM\MongoDB\Cursor; +use Doctrine\MongoDB\Database; +use Doctrine\MongoDB\Collection; +use Doctrine\ODM\MongoDB\LoggableCursor; + +/** + * ODM Query wraps the raw Doctrine MongoDB queries to add additional functionality + * and to hydrate the raw arrays of data to Doctrine document objects. + * + * @since 1.0 + * @author Jonathan H. Wage + */ +class Query extends \Doctrine\MongoDB\Query\Query +{ + const HINT_REFRESH = 1; + const HINT_SLAVE_OKAY = 2; + + /** + * The DocumentManager instance. + * + * @var DocumentManager + */ + private $dm; + + /** + * The ClassMetadata instance. + * + * @var ClassMetadata + */ + private $class; + + /** + * Whether or not to hydrate the results in to document objects. + * + * @var boolean + */ + private $hydrate = true; + + /** + * Whether or not to refresh the data for documents that are already in the identity map. + * + * @var boolean + */ + private $refresh = false; + + /** + * Array of primer Closure instances. + * + * @var array + */ + private $primers = array(); + + /** + * Whether or not to require indexes. + * + * @var bool + */ + private $requireIndexes; + + public function __construct(DocumentManager $dm, ClassMetadata $class, Database $database, Collection $collection, array $query = array(), array $options = array(), $cmd = '$', $hydrate = true, $refresh = false, array $primers = array(), $requireIndexes = null) + { + parent::__construct($database, $collection, $query, $options, $cmd); + $this->dm = $dm; + $this->class = $class; + $this->hydrate = $hydrate; + $this->refresh = $refresh; + $this->primers = $primers; + $this->requireIndexes = $requireIndexes; + } + + /** + * Gets the DocumentManager instance. + * + * @return DocumentManager $dm + */ + public function getDocumentManager() + { + return $this->dm; + } + + /** + * Gets the ClassMetadata instance. + * + * @return ClassMetadata $class + */ + public function getClass() + { + return $this->class; + } + + /** + * Sets whether or not to hydrate the documents to objects. + * + * @param boolean $bool + */ + public function setHydrate($bool) + { + $this->hydrate = $bool; + } + + /** + * Sets whether or not to refresh the documents data if it already exists in the identity map. + * + * @param boolean $bool + */ + public function setRefresh($bool) + { + $this->refresh = $bool; + } + + /** + * Gets the fields involved in this query. + * + * @return array $fields An array of fields names used in this query. + */ + public function getFieldsInQuery() + { + $extractor = new FieldExtractor($this->query['query'], $this->query['sort'], $this->cmd); + return $extractor->getFields(); + } + + /** + * Check if this query is indexed. + * + * @return bool + */ + public function isIndexed() + { + $fields = $this->getFieldsInQuery(); + foreach ($fields as $field) { + if (!$this->collection->isFieldIndexed($field)) { + return false; + } + } + return true; + } + + /** + * Gets an array of the unindexed fields in this query. + * + * @return array $unindexedFields + */ + public function getUnindexedFields() + { + $unindexedFields = array(); + $fields = $this->getFieldsInQuery(); + foreach ($fields as $field) { + if (!$this->collection->isFieldIndexed($field)) { + $unindexedFields[] = $field; + } + } + return $unindexedFields; + } + + /** + * Execute the query and returns the results. + * + * @return mixed + */ + public function execute() + { + $uow = $this->dm->getUnitOfWork(); + + if ($this->isIndexRequired() && !$this->isIndexed()) { + throw MongoDBException::queryNotIndexed($this->class->name, $this->getUnindexedFields()); + } + + $this->query['query'] = array_merge( + $this->query['query'], + $this->dm->getFilterCollection()->getFilterCriteria($this->class) + ); + $results = parent::execute(); + + $hints = array(); + if ($this->refresh) { + $hints[self::HINT_REFRESH] = true; + } + if ($this->query['slaveOkay'] === true) { + $hints[self::HINT_SLAVE_OKAY] = true; + } + + // Unwrap the BaseEagerCursor + if ($results instanceof BaseEagerCursor) { + $results = $results->getCursor(); + } + + // Convert the regular mongodb cursor to the odm cursor + if ($results instanceof BaseCursor) { + $results = $this->wrapCursor($results, $hints); + } + + // Wrap odm cursor with EagerCursor if true + if ($this->query['eagerCursor'] === true) { + $results = new EagerCursor($results, $this->dm->getUnitOfWork(), $this->class); + } + + // GeoLocationFindQuery just returns an instance of ArrayIterator so we have to + // iterator over it and hydrate each object. + if ($this->query['type'] === self::TYPE_GEO_LOCATION && $this->hydrate) { + foreach ($results as $key => $result) { + $document = $result['obj']; + if ($this->class->distance) { + $document[$this->class->distance] = $result['dis']; + } + $results[$key] = $uow->getOrCreateDocument($this->class->name, $document, $hints); + } + $results->reset(); + } + + if ($this->primers) { + $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); + foreach ($this->primers as $fieldName => $primer) { + if ($primer) { + $documentPersister->primeCollection($results, $fieldName, $primer, $hints); + } + } + } + + if ($this->hydrate && is_array($results) && isset($results['_id'])) { + // Convert a single document array to a document object + $results = $uow->getOrCreateDocument($this->class->name, $results, $hints); + } + + return $results; + } + + private function isIndexRequired() + { + if ($this->class->requireIndexes && $this->requireIndexes !== false) { + return true; + } + return $this->requireIndexes !== null ? $this->requireIndexes : false; + } + + private function wrapCursor(BaseCursor $baseCursor, array $hints) + { + if ($baseCursor instanceof BaseLoggableCursor) { + $cursor = new LoggableCursor( + $this->dm->getConnection(), + $this->collection, + $this->dm->getUnitOfWork(), + $this->class, + $baseCursor, + $baseCursor->getQuery(), + $baseCursor->getFields(), + $this->dm->getConfiguration()->getRetryQuery(), + $baseCursor->getLoggerCallable() + ); + } else { + $cursor = new Cursor( + $this->dm->getConnection(), + $this->collection, + $this->dm->getUnitOfWork(), + $this->class, + $baseCursor, + $baseCursor->getQuery(), + $baseCursor->getFields(), + $this->dm->getConfiguration()->getRetryQuery() + ); + } + $cursor->hydrate($this->hydrate); + $cursor->setHints($hints); + + return $cursor; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/SchemaManager.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/SchemaManager.php new file mode 100644 index 00000000..2908db3c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/SchemaManager.php @@ -0,0 +1,431 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; + +class SchemaManager +{ + /** + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + protected $dm; + + /** + * + * @var Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory + */ + protected $metadataFactory; + + /** + * @param Doctrine\ODM\MongoDB\DocumentManager $dm + */ + public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf) + { + $this->dm = $dm; + $this->metadataFactory = $cmf; + } + + /** + * Ensure indexes are created for all documents that can be loaded with the + * metadata factory. + * + * @param integer $timeout Timeout (ms) for acknowledged index creation + */ + public function ensureIndexes($timeout = null) + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->ensureDocumentIndexes($class->name, $timeout); + } + } + + /** + * Ensure indexes are created for all documents that can be loaded with the + * metadata factory and delete the indexes that exist in MongoDB but not the + * document metadata. + * + * @param integer $timeout Timeout (ms) for acknowledged index creation + */ + public function updateIndexes($timeout = null) + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->updateDocumentIndexes($class->name, $timeout); + } + } + + /** + * Ensure the given document's indexes are updated. + * + * @param string $documentName + * @param integer $timeout Timeout (ms) for acknowledged index creation + */ + public function updateDocumentIndexes($documentName, $timeout = null) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes or embedded documents.'); + } + + if ($documentIndexes = $this->getDocumentIndexes($documentName)) { + + $collection = $this->dm->getDocumentCollection($documentName); + $mongoIndexes = $collection->getIndexInfo(); + + /* Determine which Mongo indexes should be deleted. Exclude the ID + * index and those that are equivalent to any in the class metadata. + */ + $self = $this; + $mongoIndexes = array_filter($mongoIndexes, function($mongoIndex) use ($documentIndexes, $self) { + if ('_id_' === $mongoIndex['name']) { + return false; + } + + foreach ($documentIndexes as $documentIndex) { + if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) { + return false; + } + } + + return true; + }); + + // Delete indexes that do not exist in class metadata + foreach ($mongoIndexes as $mongoIndex) { + if (isset($mongoIndex['name'])) { + /* Note: MongoCollection::deleteIndex() cannot delete + * custom-named indexes, so use the deleteIndexes command. + */ + $collection->getDatabase()->command(array( + 'deleteIndexes' => $collection->getName(), + 'index' => $mongoIndex['name'], + )); + } + } + + $this->ensureDocumentIndexes($documentName, $timeout); + } + } + + public function getDocumentIndexes($documentName) + { + $visited = array(); + return $this->doGetDocumentIndexes($documentName, $visited); + } + + private function doGetDocumentIndexes($documentName, array &$visited) + { + if (isset($visited[$documentName])) { + return array(); + } + + $visited[$documentName] = true; + + $class = $this->dm->getClassMetadata($documentName); + $indexes = $this->prepareIndexes($class); + + // Add indexes from embedded & referenced documents + foreach ($class->fieldMappings as $fieldMapping) { + if (isset($fieldMapping['embedded']) && isset($fieldMapping['targetDocument'])) { + $embeddedIndexes = $this->doGetDocumentIndexes($fieldMapping['targetDocument'], $visited); + foreach ($embeddedIndexes as $embeddedIndex) { + foreach ($embeddedIndex['keys'] as $key => $value) { + $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value; + unset($embeddedIndex['keys'][$key]); + } + $indexes[] = $embeddedIndex; + } + + } else if (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) { + foreach ($indexes as $idx => $index) { + $newKeys = array(); + foreach ($index['keys'] as $key => $v) { + if ($key == $fieldMapping['name']) { + $key = $fieldMapping['simple'] ? $key : $key . '.$id'; + } + $newKeys[$key] = $v; + } + $indexes[$idx]['keys'] = $newKeys; + } + } + } + return $indexes; + } + + private function prepareIndexes(ClassMetadata $class) + { + $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name); + $indexes = $class->getIndexes(); + $newIndexes = array(); + + foreach ($indexes as $index) { + $newIndex = array( + 'keys' => array(), + 'options' => $index['options'] + ); + foreach ($index['keys'] as $key => $value) { + $key = $persister->prepareFieldName($key); + if (isset($class->discriminatorField) && $key === $class->discriminatorField['name']) { + // The discriminator field may have its own mapping + $newIndex['keys'][$class->discriminatorField['fieldName']] = $value; + } elseif ($class->hasField($key)) { + $mapping = $class->getFieldMapping($key); + $newIndex['keys'][$mapping['name']] = $value; + } else { + $newIndex['keys'][$key] = $value; + } + } + + $newIndexes[] = $newIndex; + } + + return $newIndexes; + } + + /** + * Ensure the given document's indexes are created. + * + * @param string $documentName + * @param integer $timeout Timeout (ms) for acknowledged index creation + */ + public function ensureDocumentIndexes($documentName, $timeout = null) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes or embedded documents.'); + } + if ($indexes = $this->getDocumentIndexes($documentName)) { + $collection = $this->dm->getDocumentCollection($class->name); + foreach ($indexes as $index) { + // TODO: Use "w" for driver versions >= 1.3.0 + if (!isset($index['options']['safe'])) { + $index['options']['safe'] = true; + } + if (!isset($index['options']['timeout']) && isset($timeout)) { + $index['options']['timeout'] = $timeout; + } + $collection->ensureIndex($index['keys'], $index['options']); + } + } + } + + /** + * Delete indexes for all documents that can be loaded with the + * metadata factory. + */ + public function deleteIndexes() + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->deleteDocumentIndexes($class->name); + } + } + + /** + * Delete the given document's indexes. + * + * @param string $documentName + */ + public function deleteDocumentIndexes($documentName) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes or embedded documents.'); + } + $this->dm->getDocumentCollection($documentName)->deleteIndexes(); + } + + /** + * Create all the mapped document collections in the metadata factory. + */ + public function createCollections() + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->createDocumentCollection($class->name); + } + } + + /** + * Create the document collection for a mapped class. + * + * @param string $documentName + */ + public function createDocumentCollection($documentName) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot create document collection for mapped super classes or embedded documents.'); + } + $this->dm->getDocumentDatabase($documentName)->createCollection( + $class->getCollection(), + $class->getCollectionCapped(), + $class->getCollectionSize(), + $class->getCollectionMax() + ); + } + + /** + * Drop all the mapped document collections in the metadata factory. + */ + public function dropCollections() + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->dropDocumentCollection($class->name); + } + } + + /** + * Drop the document collection for a mapped class. + * + * @param string $documentName + */ + public function dropDocumentCollection($documentName) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes or embedded documents.'); + } + $this->dm->getDocumentDatabase($documentName)->dropCollection( + $class->getCollection() + ); + } + + /** + * Drop all the mapped document databases in the metadata factory. + */ + public function dropDatabases() + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->dropDocumentDatabase($class->name); + } + } + + /** + * Drop the document database for a mapped class. + * + * @param string $documentName + */ + public function dropDocumentDatabase($documentName) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot drop document database for mapped super classes or embedded documents.'); + } + $this->dm->getDocumentDatabase($documentName)->drop(); + } + + /** + * Create all the mapped document databases in the metadata factory. + */ + public function createDatabases() + { + foreach ($this->metadataFactory->getAllMetadata() as $class) { + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + continue; + } + $this->createDocumentDatabase($class->name); + } + } + + /** + * Create the document database for a mapped class. + * + * @param string $documentName + */ + public function createDocumentDatabase($documentName) + { + $class = $this->dm->getClassMetadata($documentName); + if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { + throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes or embedded documents.'); + } + $this->dm->getDocumentDatabase($documentName)->execute("function() { return true; }"); + } + + /** + * Determine if an index returned by MongoCollection::getIndexInfo() can be + * considered equivalent to an index in class metadata. + * + * Indexes are considered different if: + * + * (a) Key/direction pairs differ or are not in the same order + * (b) Sparse or unique options differ + * (c) Mongo index is unique without dropDups and mapped index is unique + * with dropDups + * (d) Geospatial options differ (bits, max, min) + * + * Regarding (c), the inverse case is not a reason to delete and + * recreate the index, since dropDups only affects creation of + * the unique index. Additionally, the background option is only + * relevant to index creation and is not considered. + */ + public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex) + { + $documentIndexOptions = $documentIndex['options']; + + if ($mongoIndex['key'] !== $documentIndex['keys']) { + return false; + } + + if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) { + return false; + } + + if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) { + return false; + } + + if (!empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) && + !empty($documentIndexOptions['unique']) && !empty($documentIndexOptions['dropDups'])) { + + return false; + } + + foreach (array('bits', 'max', 'min') as $option) { + if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) { + return false; + } + + if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) && + $mongoIndex[$option] !== $documentIndexOptions[$option]) { + + return false; + } + } + + return true; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/ClearCache/MetadataCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/ClearCache/MetadataCommand.php new file mode 100644 index 00000000..a709fc49 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Henrik Westphal + */ +class MetadataCommand extends Command +{ + /** + * @see \Symfony\Component\Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:clear-cache:metadata') + ->setDescription('Clear all metadata cache of the various cache drivers.') + ->setDefinition(array()) + ->setHelp(<<getHelper('dm')->getDocumentManager(); + $cacheDriver = $dm->getConfiguration()->getMetadataCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Metadata cache driver is configured on given DocumentManager.'); + } + + if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Metadata cache entries' . PHP_EOL); + + $success = $cacheDriver->deleteAll(); + + if ($success) { + $output->write('The cache entries were successfully deleted.' . PHP_EOL); + } else { + $output->write('No entries to be deleted.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateDocumentsCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateDocumentsCommand.php new file mode 100644 index 00000000..fef704ce --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateDocumentsCommand.php @@ -0,0 +1,159 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ODM\MongoDB\Tools\Console\MetadataFilter; +use Doctrine\ODM\MongoDB\Tools\DocumentGenerator; +use Doctrine\ODM\MongoDB\Tools\DisconnectedClassMetadataFactory; + +/** + * Command to generate document classes and method stubs from your mapping information. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateDocumentsCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:generate:documents') + ->setDescription('Generate document classes and method stubs from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match documents that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your document classes.' + ), + new InputOption( + 'generate-annotations', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate annotation metadata on documents.', false + ), + new InputOption( + 'generate-methods', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate stub methods on documents.', true + ), + new InputOption( + 'regenerate-documents', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should regenerate document if it exists.', false + ), + new InputOption( + 'update-documents', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should only update document if it exists.', true + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated document classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<--update-documents or --regenerate-documents flags your exisiting +code gets overwritten. The DocumentGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the document code if you are using documents as Data +Access Objects only and dont put much additional logic on them. If you are +however putting much more logic on the documents you should refrain from using +the document-generator and code your documents manually. + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the document +code manually for inheritance to work! +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $dm = $this->getHelper('dm')->getDocumentManager(); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setDocumentManager($dm); + $cmf->setConfiguration($dm->getConfiguration()); + $metadatas = $cmf->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Documents destination directory '%s' does not exist.", $destPath) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Documents destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + // Create DocumentGenerator + $documentGenerator = new DocumentGenerator(); + + $documentGenerator->setGenerateAnnotations($input->getOption('generate-annotations')); + $documentGenerator->setGenerateStubMethods($input->getOption('generate-methods')); + $documentGenerator->setRegenerateDocumentIfExists($input->getOption('regenerate-documents')); + $documentGenerator->setUpdateDocumentIfExists($input->getOption('update-documents')); + $documentGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $documentGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing document "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Documents + $documentGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Document classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateHydratorsCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateHydratorsCommand.php new file mode 100644 index 00000000..ac6c8ad6 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateHydratorsCommand.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ODM\MongoDB\Tools\Console\MetadataFilter; + +/** + * Command to (re)generate the hydrator classes used by doctrine. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateHydratorsCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:generate:hydrators') + ->setDescription('Generates hydrator classes for document classes.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match documents that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::OPTIONAL, + 'The path to generate your hydrator classes. If none is provided, it will attempt to grab from configuration.' + ), + )) + ->setHelp(<<getHelper('dm')->getDocumentManager(); + + $metadatas = $dm->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + if (($destPath = $input->getArgument('dest-path')) === null) { + $destPath = $dm->getConfiguration()->getHydratorDir(); + } + + if ( ! is_dir($destPath)) { + mkdir($destPath, 0777, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Hydrators destination directory '%s' does not exist.", $destPath) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Hydrators destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing document "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Hydrators + $dm->getHydratorFactory()->generateHydratorClasses($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Hydrator classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateProxiesCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateProxiesCommand.php new file mode 100644 index 00000000..2574dfd7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateProxiesCommand.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ODM\MongoDB\Tools\Console\MetadataFilter; + +/** + * Command to (re)generate the proxy classes used by doctrine. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateProxiesCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:generate:proxies') + ->setDescription('Generates proxy classes for document classes.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match documents that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::OPTIONAL, + 'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.' + ), + )) + ->setHelp(<<getHelper('dm')->getDocumentManager(); + + $metadatas = $dm->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + if (($destPath = $input->getArgument('dest-path')) === null) { + $destPath = $dm->getConfiguration()->getProxyDir(); + } + + if ( ! is_dir($destPath)) { + mkdir($destPath, 0777, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $destPath) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing document "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Proxies + $dm->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateRepositoriesCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateRepositoriesCommand.php new file mode 100644 index 00000000..ec60dace --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ODM\MongoDB\Tools\Console\MetadataFilter; +use Doctrine\ODM\MongoDB\Tools\DocumentRepositoryGenerator; + +/** + * Command to generate repository classes for mapping information. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateRepositoriesCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:generate:repositories') + ->setDescription('Generate repository classes from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match documents that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.' + ) + )) + ->setHelp(<<getHelper('dm')->getDocumentManager(); + + $metadatas = $dm->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Documents destination directory '%s' does not exist.", $destPath) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Documents destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + $numRepositories = 0; + $generator = new DocumentRepositoryGenerator(); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->write( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) . PHP_EOL + ); + + $generator->writeDocumentRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->write(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Repository classes were found to be processed.' . PHP_EOL); + } + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/QueryCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/QueryCommand.php new file mode 100644 index 00000000..b5bff2b8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/QueryCommand.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; + +/** + * Command to query mongodb and inspect the outputted results from your document classes. + * + * @since 1.0 + * @author Jonathan Wage + */ +class QueryCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('odm:query') + ->setDescription('Query mongodb and inspect the outputted results from your document classes.') + ->setDefinition(array( + new InputArgument( + 'class', InputArgument::REQUIRED, + 'The class to query.' + ), + new InputArgument( + 'query', InputArgument::REQUIRED, + 'The query to execute and output the results for.' + ), + new InputOption( + 'hydrate', null, InputOption::VALUE_NONE, + 'Whether or not to hydrate the results in to document objects.' + ), + new InputOption( + 'skip', null, InputOption::VALUE_REQUIRED, + 'The number of documents to skip in the cursor.' + ), + new InputOption( + 'limit', null, InputOption::VALUE_REQUIRED, + 'The number of documents to return.' + ), + new InputOption( + 'depth', null, InputOption::VALUE_REQUIRED, + 'Dumping depth of Document graph.', 7 + ) + )) + ->setHelp(<<getHelper('dm')->getDocumentManager(); + $query = json_decode($input->getArgument('query')); + $cursor = $dm->getRepository($input->getArgument('class'))->findBy((array) $query); + $cursor->hydrate((bool) $input->getOption('hydrate')); + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contain an integer value"); + } + + if (($skip = $input->getOption('skip')) !== null) { + if ( ! is_numeric($skip)) { + throw new \LogicException("Option 'skip' must contain an integer value"); + } + + $cursor->skip((int) $skip); + } + + if (($limit = $input->getOption('limit')) !== null) { + if ( ! is_numeric($limit)) { + throw new \LogicException("Option 'limit' must contain an integer value"); + } + + $cursor->limit((int) $limit); + } + + $resultSet = $cursor->toArray(); + + \Doctrine\Common\Util\Debug::dump($resultSet, $depth); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php new file mode 100644 index 00000000..91173a95 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command\Schema; + +use Doctrine\ODM\MongoDB\SchemaManager; +use Symfony\Component\Console\Command\Command; + +/** + * @author Bulat Shakirzyanov + */ +abstract class AbstractCommand extends Command +{ + const DB = 'db'; + const COLLECTION = 'collection'; + const INDEX = 'index'; + + abstract protected function processDocumentCollection(SchemaManager $sm, $document); + abstract protected function processCollection(SchemaManager $sm); + abstract protected function processDocumentDb(SchemaManager $sm, $document); + abstract protected function processDb(SchemaManager $sm); + abstract protected function processDocumentIndex(SchemaManager $sm, $document); + abstract protected function processIndex(SchemaManager $sm); + + /** + * @return Doctrine\ODM\MongoDB\SchemaManager + */ + protected function getSchemaManager() + { + return $this->getDocumentManager()->getSchemaManager(); + } + + /** + * @return Doctrine\ODM\MongoDB\DocumentManager + */ + protected function getDocumentManager() + { + return $this->getHelper('documentManager')->getDocumentManager(); + } + + /** + * @return Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory + */ + protected function getMetadataFactory() + { + return $this->getDocumentManager()->getMetadataFactory(); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/CreateCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/CreateCommand.php new file mode 100644 index 00000000..6b586a46 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/CreateCommand.php @@ -0,0 +1,125 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command\Schema; + +use Doctrine\ODM\MongoDB\SchemaManager; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Bulat Shakirzyanov + */ +class CreateCommand extends AbstractCommand +{ + private $createOrder = array(self::DB, self::COLLECTION, self::INDEX); + + private $timeout; + + protected function configure() + { + $this + ->setName('odm:schema:create') + ->addOption('class', 'c', InputOption::VALUE_REQUIRED, 'Document class to process (default: all classes)') + ->addOption('timeout', 't', InputOption::VALUE_OPTIONAL, 'Timeout (ms) for acknowledged index creation') + ->addOption(self::DB, null, InputOption::VALUE_NONE, 'Create databases') + ->addOption(self::COLLECTION, null, InputOption::VALUE_NONE, 'Create collections') + ->addOption(self::INDEX, null, InputOption::VALUE_NONE, 'Create indexes') + ->setDescription('Create databases, collections and indexes for your documents') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + foreach ($this->createOrder as $option) { + if ($input->getOption($option)) { + $create[] = $option; + } + } + + // Default to the full creation order if no options were specified + $create = empty($create) ? $this->createOrder : $create; + + $class = $input->getOption('class'); + + $timeout = $input->getOption('timeout'); + $this->timeout = isset($timeout) ? (int) $timeout : null; + + $sm = $this->getSchemaManager(); + + foreach ($create as $option) { + try { + if (isset($class)) { + $this->{'processDocument' . ucfirst($option)}($sm, $class); + } else { + $this->{'process' . ucfirst($option)}($sm); + } + $output->writeln(sprintf( + 'Created %s%s for %s', + $option, + (isset($class) ? (self::INDEX === $option ? '(es)' : '') : (self::INDEX === $option ? 'es' : 's')), + (isset($class) ? $class : 'all classes') + )); + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + } + } + } + + protected function processDocumentCollection(SchemaManager $sm, $document) + { + $sm->createDocumentCollection($document); + } + + protected function processCollection(SchemaManager $sm) + { + $sm->createCollections(); + } + + protected function processDocumentDb(SchemaManager $sm, $document) + { + $sm->createDocumentDatabase($document); + } + + protected function processDb(SchemaManager $sm) + { + $sm->createDatabases(); + } + + protected function processDocumentIndex(SchemaManager $sm, $document) + { + $sm->ensureDocumentIndexes($document, $this->timeout); + } + + protected function processIndex(SchemaManager $sm) + { + $sm->ensureIndexes($this->timeout); + } + + protected function processDocumentProxy(SchemaManager $sm, $document) + { + $this->getDocumentManager()->getProxyFactory()->generateProxyClasses(array($this->getMetadataFactory()->getMetadataFor($document))); + } + + protected function processProxy(SchemaManager $sm) + { + $this->getDocumentManager()->getProxyFactory()->generateProxyClasses($this->getMetadataFactory()->getAllMetadata()); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/DropCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/DropCommand.php new file mode 100644 index 00000000..7de21748 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/DropCommand.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command\Schema; + +use Doctrine\ODM\MongoDB\SchemaManager; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Bulat Shakirzyanov + */ +class DropCommand extends AbstractCommand +{ + private $dropOrder = array(self::INDEX, self::COLLECTION, self::DB); + + protected function configure() + { + $this + ->setName('odm:schema:drop') + ->addOption('class', 'c', InputOption::VALUE_REQUIRED, 'Document class to process (default: all classes)') + ->addOption(self::DB, null, InputOption::VALUE_NONE, 'Drop databases') + ->addOption(self::COLLECTION, null, InputOption::VALUE_NONE, 'Drop collections') + ->addOption(self::INDEX, null, InputOption::VALUE_NONE, 'Drop indexes') + ->setDescription('Drop databases, collections and indexes for your documents') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + foreach ($this->dropOrder as $option) { + if ($input->getOption($option)) { + $drop[] = $option; + } + } + + // Default to the full drop order if no options were specified + $drop = empty($drop) ? $this->dropOrder : $drop; + + $class = $input->getOption('class'); + $sm = $this->getSchemaManager(); + + foreach ($drop as $option) { + try { + if (isset($class)) { + $this->{'processDocument' . ucfirst($option)}($sm, $class); + } else { + $this->{'process' . ucfirst($option)}($sm); + } + $output->writeln(sprintf( + 'Dropped %s%s for %s', + $option, + (isset($class) ? (self::INDEX === $option ? '(es)' : '') : (self::INDEX === $option ? 'es' : 's')), + (isset($class) ? $class : 'all classes') + )); + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + } + } + } + + protected function processDocumentCollection(SchemaManager $sm, $document) + { + $sm->dropDocumentCollection($document); + } + + protected function processCollection(SchemaManager $sm) + { + $sm->dropCollections(); + } + + protected function processDocumentDb(SchemaManager $sm, $document) + { + $sm->dropDocumentDatabase($document); + } + + protected function processDb(SchemaManager $sm) + { + $sm->dropDatabases(); + } + + protected function processDocumentIndex(SchemaManager $sm, $document) + { + $sm->deleteDocumentIndexes($document); + } + + protected function processIndex(SchemaManager $sm) + { + $sm->deleteIndexes(); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/UpdateCommand.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/UpdateCommand.php new file mode 100644 index 00000000..e120ac2c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/UpdateCommand.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Command\Schema; + +use Doctrine\ODM\MongoDB\SchemaManager; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Chris Jones + * @author Michał Dąbrowski + */ +class UpdateCommand extends AbstractCommand +{ + private $dropOrder = array(self::INDEX, self::COLLECTION, self::DB); + + private $timeout; + + protected function configure() + { + $this + ->setName('odm:schema:update') + ->addOption('class', 'c', InputOption::VALUE_OPTIONAL, 'Document class to process (default: all classes)') + ->addOption('timeout', 't', InputOption::VALUE_OPTIONAL, 'Timeout (ms) for acknowledged index creation') + ->setDescription('Update indexes for your documents') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $class = $input->getOption('class'); + + $timeout = $input->getOption('timeout'); + $this->timeout = isset($timeout) ? (int) $timeout : null; + + $sm = $this->getSchemaManager(); + + try { + if (isset($class)) { + $this->processDocumentIndex($sm, $class); + $output->writeln(sprintf('Updated index(es) for %s', $class)); + } else { + $this->processIndex($sm); + $output->writeln('Updated indexes for all classes'); + } + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + } + } + + protected function processDocumentIndex(SchemaManager $sm, $document) + { + $sm->updateDocumentIndexes($document, $this->timeout); + } + + protected function processIndex(SchemaManager $sm) + { + $sm->updateIndexes($this->timeout); + } + + protected function processDocumentCollection(SchemaManager $sm, $document) + { + throw new \BadMethodCallException('Cannot update a document collection'); + } + + protected function processCollection(SchemaManager $sm) + { + throw new \BadMethodCallException('Cannot update a collection'); + } + + protected function processDocumentDb(SchemaManager $sm, $document) + { + throw new \BadMethodCallException('Cannot update a document database'); + } + + protected function processDb(SchemaManager $sm) + { + throw new \BadMethodCallException('Cannot update a database'); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Helper/DocumentManagerHelper.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Helper/DocumentManagerHelper.php new file mode 100644 index 00000000..50d865b3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/Helper/DocumentManagerHelper.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\ODM\MongoDB\DocumentManager; + +/** + * @author Bulat Shakirzyanov + */ +class DocumentManagerHelper extends Helper +{ + protected $dm; + public function __construct(DocumentManager $dm) + { + $this->dm = $dm; + } + public function getDocumentManager() + { + return $this->dm; + } + public function getName() + { + return 'documentManager'; + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/MetadataFilter.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/MetadataFilter.php new file mode 100644 index 00000000..88db86e7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/Console/MetadataFilter.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataFilter extends \FilterIterator implements \Countable +{ + /** + * Filter Metadatas by one or more filter options. + * + * @param array $metadatas + * @param array|string $filter + * @return array + */ + static public function filter(array $metadatas, $filter) + { + $metadatas = new MetadataFilter(new \ArrayIterator($metadatas), $filter); + return iterator_to_array($metadatas); + } + + private $_filter = array(); + + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->_filter = (array)$filter; + parent::__construct($metadata); + } + + public function accept() + { + if (count($this->_filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->_filter AS $filter) { + if (strpos($metadata->name, $filter) !== false) { + return true; + } + } + return false; + } + + public function count() + { + return count($this->getInnerIterator()); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DisconnectedClassMetadataFactory.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DisconnectedClassMetadataFactory.php new file mode 100644 index 00000000..0f1d5d99 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; + +/** + * The DisconnectedClassMetadataFactory is used to create ClassMetadata objects + * that do not require the document class actually exist. This allows us to + * load some mapping information and use it to do things like generate code + * from the mapping information. + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DisconnectedClassMetadataFactory extends ClassMetadataFactory +{ + /** + * @override + */ + protected function newClassMetadataInstance($className) + { + $metadata = new ClassMetadataInfo($className); + if (strpos($className, "\\") !== false) { + $metadata->namespace = strrev(substr( strrev($className), strpos(strrev($className), "\\")+1 )); + } else { + $metadata->namespace = ""; + } + return $metadata; + } + + /** + * @override + */ + protected function getParentClasses($name) + { + return array(); + } + + /** + * @override + */ + protected function validateIdentifier($class) + { + // do nothing as the DisconnectedClassMetadataFactory cannot validate an inherited id + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentGenerator.php new file mode 100644 index 00000000..eeffa6c8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentGenerator.php @@ -0,0 +1,936 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools; + +use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; + +/** + * Generic class used to generate PHP5 document classes from ClassMetadataInfo instances + * + * [php] + * $classes = $dm->getClassMetadataInfoFactory()->getAllMetadata(); + * + * $generator = new \Doctrine\ODM\MongoDB\Tools\DocumentGenerator(); + * $generator->setGenerateAnnotations(true); + * $generator->setGenerateStubMethods(true); + * $generator->setRegenerateDocumentIfExists(false); + * $generator->setUpdateDocumentIfExists(true); + * $generator->generate($classes, '/path/to/generate/documents'); + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DocumentGenerator +{ + /** + * @var bool + */ + private $backupExisting = true; + + /** The extension to use for written php files */ + private $extension = '.php'; + + /** Whether or not the current ClassMetadataInfo instance is new or old */ + private $isNew = true; + + private $staticReflection = array(); + + /** Number of spaces to use for indention in generated code */ + private $numSpaces = 4; + + /** The actual spaces to use for indention */ + private $spaces = ' '; + + /** The class all generated documents should extend */ + private $classToExtend; + + /** Whether or not to generation annotations */ + private $generateAnnotations = false; + + /** Whether or not to generated sub methods */ + private $generateDocumentStubMethods = false; + + /** Whether or not to update the document class if it exists already */ + private $updateDocumentIfExists = false; + + /** Whether or not to re-generate document class if it exists already */ + private $regenerateDocumentIfExists = false; + + private static $classTemplate = +' + + + + + +{ + +}'; + + private static $getMethodTemplate = +'/** + * + * + * @return $ + */ +public function () +{ +return $this->; +}'; + + private static $setMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this-> = $; +return $this; +}'; + + private static $addMethodTemplate = +'/** + * + * + * @param $ + */ +public function ($) +{ +$this->[] = $; +}'; + + private static $removeMethodTemplate = +'/** +* +* +* @param +*/ +public function ($) +{ +$this->->removeElement($); +}'; + + private static $lifecycleCallbackMethodTemplate = +' +public function () +{ +// Add your code here +}'; + + private static $constructorMethodTemplate = +'public function __construct() +{ + +} +'; + + /** + * Generate and write document classes for the given array of ClassMetadataInfo instances + * + * @param array $metadatas + * @param string $outputDirectory + * @return void + */ + public function generate(array $metadatas, $outputDirectory) + { + foreach ($metadatas as $metadata) { + $this->writeDocumentClass($metadata, $outputDirectory); + } + } + + /** + * Generated and write document class to disk for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * @return void + */ + public function writeDocumentClass(ClassMetadataInfo $metadata, $outputDirectory) + { + $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateDocumentIfExists); + + if ( ! $this->isNew) { + $this->parseTokensInDocumentFile($path); + } + + if ($this->backupExisting && file_exists($path)) { + $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . '~' ; + if (!copy($path, $backupPath)) { + throw new \RuntimeException("Attempt to backup overwritten document file but copy operation failed."); + } + } + + // If document doesn't exist or we're re-generating the documents entirely + if ($this->isNew) { + file_put_contents($path, $this->generateDocumentClass($metadata)); + // If document exists and we're allowed to update the document class + } else if ( ! $this->isNew && $this->updateDocumentIfExists) { + file_put_contents($path, $this->generateUpdatedDocumentClass($metadata, $path)); + } + } + + /** + * Generate a PHP5 Doctrine 2 document class from the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $code + */ + public function generateDocumentClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '', + '' + ); + + $replacements = array( + $this->generateDocumentNamespace($metadata), + $this->generateDocumentImports($metadata), + $this->generateDocumentDocBlock($metadata), + $this->generateDocumentClassName($metadata), + $this->generateDocumentBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, self::$classTemplate); + return str_replace('', $this->spaces, $code); + } + + /** + * Generate the updated code for the given ClassMetadataInfo and document at path + * + * @param ClassMetadataInfo $metadata + * @param string $path + * @return string $code; + */ + public function generateUpdatedDocumentClass(ClassMetadataInfo $metadata, $path) + { + $currentCode = file_get_contents($path); + + $body = $this->generateDocumentBody($metadata); + $body = str_replace('', $this->spaces, $body); + $last = strrpos($currentCode, '}'); + + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}\n"; + } + + /** + * Set the number of spaces the exported class should have + * + * @param integer $numSpaces + * @return void + */ + public function setNumSpaces($numSpaces) + { + $this->spaces = str_repeat(' ', $numSpaces); + $this->numSpaces = $numSpaces; + } + + /** + * Set the extension to use when writing php files to disk + * + * @param string $extension + * @return void + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Set the name of the class the generated classes should extend from + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->classToExtend = $classToExtend; + } + + /** + * Set whether or not to generate annotations for the document + * + * @param bool $bool + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->generateAnnotations = $bool; + } + + /** + * Set whether or not to try and update the document if it already exists + * + * @param bool $bool + * @return void + */ + public function setUpdateDocumentIfExists($bool) + { + $this->updateDocumentIfExists = $bool; + } + + /** + * Set whether or not to regenerate the document if it exists + * + * @param bool $bool + * @return void + */ + public function setRegenerateDocumentIfExists($bool) + { + $this->regenerateDocumentIfExists = $bool; + } + + /** + * Set whether or not to generate stub methods for the document + * + * @param bool $bool + * @return void + */ + public function setGenerateStubMethods($bool) + { + $this->generateDocumentStubMethods = $bool; + } + + /** + * Should an existing document be backed up if it already exists? + */ + public function setBackupExisting($bool) + { + $this->backupExisting = $bool; + } + + private function generateDocumentNamespace(ClassMetadataInfo $metadata) + { + if ($this->hasNamespace($metadata)) { + return 'namespace ' . $this->getNamespace($metadata) .';'; + } + } + + private function generateDocumentClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->getClassName($metadata) . + ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); + } + + private function generateDocumentBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->generateDocumentFieldMappingProperties($metadata); + $associationMappingProperties = $this->generateDocumentAssociationMappingProperties($metadata); + $stubMethods = $this->generateDocumentStubMethods ? $this->generateDocumentStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->generateDocumentLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->generateDocumentConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = "\n".$lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + private function generateDocumentConstructor(ClassMetadataInfo $metadata) + { + if ($this->hasMethod('__construct', $metadata)) { + return ''; + } + + $collections = array(); + foreach ($metadata->fieldMappings AS $mapping) { + if ($mapping['type'] === ClassMetadataInfo::MANY) { + $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; + } + } + if ($collections) { + return $this->prefixCodeWithSpaces(str_replace("", $this->spaces.implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate)); + } + return ''; + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * @param string $path + */ + private function parseTokensInDocumentFile($path) + { + $tokens = token_get_all(file_get_contents($path)); + $lastSeenNamespace = ''; + $lastSeenClass = false; + + for ($i = 0; $i < count($tokens); $i++) { + $token = $tokens[$i]; + if ($token[0] == T_NAMESPACE) { + $peek = $i; + $lastSeenNamespace = ''; + while (isset($tokens[++$peek])) { + if (';' == $tokens[$peek]) { + break; + } elseif (is_array($tokens[$peek]) && in_array($tokens[$peek][0], array(T_STRING, T_NS_SEPARATOR))) { + $lastSeenNamespace .= $tokens[$peek][1]; + } + } + } else if ($token[0] == T_CLASS) { + $lastSeenClass = $lastSeenNamespace . '\\' . $tokens[$i+2][1]; + $this->staticReflection[$lastSeenClass]['properties'] = array(); + $this->staticReflection[$lastSeenClass]['methods'] = array(); + } else if ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1]; + } else if ($tokens[$i+2][0] == '&' && $tokens[$i+3][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1]; + } + } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) { + $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1); + } + } + } + + private function hasProperty($property, ClassMetadataInfo $metadata) + { + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($property, $this->staticReflection[$metadata->name]['properties']) + ); + } + + private function hasMethod($method, ClassMetadataInfo $metadata) + { + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($method, $this->staticReflection[$metadata->name]['methods']) + ); + } + + private function hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + private function extendsClass() + { + return $this->classToExtend ? true : false; + } + + private function getClassToExtend() + { + return $this->classToExtend; + } + + private function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + + return '\\' . $refl->getName(); + } + + private function getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + private function getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + private function generateDocumentImports(ClassMetadataInfo $metadata) + { + if ($this->generateAnnotations) { + return 'use Doctrine\\ODM\\MongoDB\\Mapping\\Annotations as ODM;'; + } + } + + private function generateDocumentDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * '.$metadata->name; + + if ($this->generateAnnotations) { + $lines[] = ' *'; + + if ($metadata->isMappedSuperclass) { + $lines[] = ' * @ODM\\MappedSupperClass'; + } else if ($metadata->isEmbeddedDocument) { + $lines[] = ' * @ODM\\EmbeddedDocument'; + } else { + $lines[] = ' * @ODM\\Document'; + } + + $document = array(); + if (! $metadata->isMappedSuperclass && ! $metadata->isEmbeddedDocument) { + if ($metadata->collection) { + $document[] = ' * collection="' . $metadata->collection . '"'; + } + if ($metadata->customRepositoryClassName) { + $document[] = ' * repositoryClass="' . $metadata->customRepositoryClassName . '"'; + } + } + if ($metadata->indexes) { + $indexes = array(); + $indexLines = array(); + $indexLines[] = " * indexes={"; + foreach ($metadata->indexes as $index) { + $keys = array(); + foreach ($index['keys'] as $key => $value) { + $keys[] = '"'.$key.'"="'.$value.'"'; + } + $options = array(); + foreach ($index['options'] as $key => $value) { + $options[] = '"'.$key.'"="'.$value.'"'; + } + $indexes[] = '@ODM\\Index(keys={' . implode(', ', $keys) . '}, options={' . implode(', ', $options) . '})'; + } + $indexLines[] = "\n * " . implode(",\n * ", $indexes); + $indexLines[] = "\n * }"; + + $document[] = implode(null, $indexLines); + } + + if ($document) { + $lines[count($lines) - 1] .= '('; + $lines[] = implode(",\n", $document); + $lines[] = ' * )'; + } + + $methods = array( + 'generateInheritanceAnnotation', + 'generateDiscriminatorFieldAnnotation', + 'generateDiscriminatorMapAnnotation', + 'generateChangeTrackingPolicyAnnotation' + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + } + + $lines[] = ' */'; + return implode("\n", $lines); + } + + private function generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@ODM\\InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + private function generateDiscriminatorFieldAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrField = $metadata->discriminatorField; + return '@ODM\\DiscriminatorField(fieldName="' . $discrField['fieldName'] . '")'; + } + } + + private function generateDiscriminatorMapAnnotation(ClassMetadataInfo $metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $inheritanceClassMap = array(); + + foreach ($metadata->discriminatorMap as $type => $class) { + $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"'; + } + + return '@ODM\\DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; + } + } + + private function generateChangeTrackingPolicyAnnotation(ClassMetadataInfo $metadata) + { + return '@ODM\\ChangeTrackingPolicy("' . $this->getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . '")'; + } + + private function generateDocumentStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if (isset($fieldMapping['id'])) { + if ($metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) { + if ($code = $this->generateDocumentStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + if ($code = $code = $this->generateDocumentStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } else if ( ! isset($fieldMapping['association'])) { + if ($code = $code = $this->generateDocumentStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + if ($code = $code = $this->generateDocumentStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } else if ($fieldMapping['type'] === ClassMetadataInfo::ONE) { + if ($code = $this->generateDocumentStubMethod($metadata, 'set', $fieldMapping['fieldName'], isset($fieldMapping['targetDocument']) ? $fieldMapping['targetDocument'] : null)) { + $methods[] = $code; + } + if ($code = $this->generateDocumentStubMethod($metadata, 'get', $fieldMapping['fieldName'], isset($fieldMapping['targetDocument']) ? $fieldMapping['targetDocument'] : null)) { + $methods[] = $code; + } + } else if ($fieldMapping['type'] === ClassMetadataInfo::MANY) { + if ($code = $this->generateDocumentStubMethod($metadata, 'add', $fieldMapping['fieldName'], isset($fieldMapping['targetDocument']) ? $fieldMapping['targetDocument'] : null)) { + $methods[] = $code; + } + if ($code = $this->generateDocumentStubMethod($metadata, 'remove', $fieldMapping['fieldName'], isset($fieldMapping['targetDocument']) ? $fieldMapping['targetDocument'] : null)) { + $methods[] = $code; + } + if ($code = $this->generateDocumentStubMethod($metadata, 'get', $fieldMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + private function generateDocumentLifecycleCallbackMethods(ClassMetadataInfo $metadata) + { + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $methods = array(); + + foreach ($metadata->lifecycleCallbacks as $name => $callbacks) { + foreach ($callbacks as $callback) { + if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + return ""; + } + + private function generateDocumentAssociationMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + if ( ! isset($fieldMapping['association'])) { + continue; + } + + $lines[] = $this->generateAssociationMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . 'protected $' . $fieldMapping['fieldName'] + . ($fieldMapping['type'] === ClassMetadataInfo::MANY ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function generateDocumentFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + if (isset($fieldMapping['association']) && $fieldMapping['association']) { + continue; + } + + $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . 'protected $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function generateDocumentStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null) + { + $methodName = $type . Inflector::classify($fieldName); + + // TODO: This needs actual plural -> singular conversion + if (in_array($type, array('add', 'remove')) && substr($methodName, -1) == 's') { + $methodName = substr($methodName, 0, -1); + } + + if ($this->hasMethod($methodName, $metadata)) { + return; + } + + $var = sprintf('%sMethodTemplate', $type); + $template = self::$$var; + + $variableType = $typeHint ? $typeHint . ' ' : null; + + $types = \Doctrine\ODM\MongoDB\Mapping\Types\Type::getTypesMap(); + $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null; + + $replacements = array( + '' => ucfirst($type) . ' ' . $fieldName, + '' => $methodTypeHint, + '' => $variableType, + '' => Inflector::camelize($fieldName), + '' => $methodName, + '' => $fieldName, + '' => '\\' . $this->getClassName($metadata), + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->prefixCodeWithSpaces($method); + } + + private function generateLifecycleCallbackMethod($name, $methodName, $metadata) + { + if ($this->hasMethod($methodName, $metadata)) { + return; + } + + $replacements = array( + '' => $this->generateAnnotations ? '/** @ODM\\'.ucfirst($name).' */' : '', + '' => $methodName, + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + self::$lifecycleCallbackMethodTemplate + ); + + return $this->prefixCodeWithSpaces($method); + } + + private function generateAssociationMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var ' . (isset($fieldMapping['targetDocument']) ? $fieldMapping['targetDocument'] : 'object'); + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $type = null; + switch ($fieldMapping['association']) { + case ClassMetadataInfo::EMBED_ONE: + $type = 'EmbedOne'; + break; + case ClassMetadataInfo::EMBED_MANY: + $type = 'EmbedMany'; + break; + case ClassMetadataInfo::REFERENCE_ONE: + $type = 'ReferenceOne'; + break; + case ClassMetadataInfo::REFERENCE_MANY: + $type = 'ReferenceMany'; + break; + } + $typeOptions = array(); + + if (isset($fieldMapping['targetDocument'])) { + $typeOptions[] = 'targetDocument="' . $fieldMapping['targetDocument'] . '"'; + } + + if (isset($fieldMapping['cascade']) && $fieldMapping['cascade']) { + $cascades = array(); + + if ($fieldMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($fieldMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($fieldMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($fieldMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($fieldMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + $lines[] = $this->spaces . ' * @ODM\\' . $type . '(' . implode(', ', $typeOptions) . ')'; + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + private function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $fieldMapping['strategy'] = isset($fieldMapping['strategy']) ? $fieldMapping['strategy'] : ClassMetadataInfo::GENERATOR_TYPE_AUTO; + if ($fieldMapping['strategy'] === ClassMetadataInfo::GENERATOR_TYPE_AUTO) { + $lines[] = $this->spaces . ' * @var MongoId $' . $fieldMapping['fieldName']; + } elseif ($fieldMapping['strategy'] === ClassMetadataInfo::GENERATOR_TYPE_INCREMENT) { + $lines[] = $this->spaces . ' * @var integer $' . $fieldMapping['fieldName']; + } elseif ($fieldMapping['strategy'] === ClassMetadataInfo::GENERATOR_TYPE_UUID) { + $lines[] = $this->spaces . ' * @var string $' . $fieldMapping['fieldName']; + } elseif ($fieldMapping['strategy'] === ClassMetadataInfo::GENERATOR_TYPE_NONE) { + $lines[] = $this->spaces . ' * @var $' . $fieldMapping['fieldName']; + } else { + $lines[] = $this->spaces . ' * @var $' . $fieldMapping['fieldName']; + } + } else { + $lines[] = $this->spaces . ' * @var ' . $fieldMapping['type'] . ' $' . $fieldMapping['fieldName']; + } + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $field = array(); + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + if (isset($fieldMapping['strategy'])) { + $field[] = 'strategy="' . $this->getIdGeneratorTypeString($metadata->generatorType) . '"'; + } + $lines[] = $this->spaces . ' * @ODM\\Id(' . implode(', ', $field) . ')'; + } else { + if (isset($fieldMapping['name'])) { + $field[] = 'name="' . $fieldMapping['name'] . '"'; + } + + if (isset($fieldMapping['type'])) { + $field[] = 'type="' . $fieldMapping['type'] . '"'; + } + + if (isset($fieldMapping['nullable']) && $fieldMapping['nullable'] === true) { + $field[] = 'nullable=' . var_export($fieldMapping['nullable'], true); + } + if (isset($fieldMapping['options'])) { + $options = array(); + foreach ($fieldMapping['options'] as $key => $value) { + $options[] = '"' . $key . '" = "' . $value . '"'; + } + $field[] = "options={".implode(', ', $options)."}"; + } + $lines[] = $this->spaces . ' * @ODM\\Field(' . implode(', ', $field) . ')'; + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $lines[] = $this->spaces . ' * @ODM\\Version'; + } + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + private function prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key]; + } + + return implode("\n", $lines); + } + + private function getInheritanceTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION: + return 'SINGLE_COLLECTION'; + + case ClassMetadataInfo::INHERITANCE_TYPE_COLLECTION_PER_CLASS: + return 'COLLECTION_PER_CLASS'; + + default: + throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type); + } + } + + private function getChangeTrackingPolicyString($policy) + { + switch ($policy) { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + + default: + throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy); + } + } + + private function getIdGeneratorTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + + case ClassMetadataInfo::GENERATOR_TYPE_INCREMENT: + return 'INCREMENT'; + + case ClassMetadataInfo::GENERATOR_TYPE_UUID: + return 'UUID'; + + case ClassMetadataInfo::GENERATOR_TYPE_NONE: + return 'NONE'; + + default: + throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentRepositoryGenerator.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentRepositoryGenerator.php new file mode 100644 index 00000000..d1416d70 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Tools/DocumentRepositoryGenerator.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ODM\MongoDB\Tools; + +/** + * Class to generate document repository classes + * + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DocumentRepositoryGenerator +{ + protected static $template = +'; + +use Doctrine\ODM\MongoDB\DocumentRepository; + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends DocumentRepository +{ +}'; + + public function generateDocumentRepositoryClass($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + + $variables = array( + '' => $namespace, + '' => $className + ); + return str_replace(array_keys($variables), array_values($variables), self::$template); + } + + public function writeDocumentRepositoryClass($fullClassName, $outputDirectory) + { + $code = $this->generateDocumentRepositoryClass($fullClassName); + + $path = $outputDirectory . DIRECTORY_SEPARATOR + . str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php'; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/UnitOfWork.php new file mode 100644 index 00000000..26f5a56d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/UnitOfWork.php @@ -0,0 +1,2782 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +use Doctrine\Common\EventManager; +use Doctrine\ODM\MongoDB\Internal\CommitOrderCalculator; +use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Proxy\Proxy; +use Doctrine\ODM\MongoDB\Mapping\Types\Type; +use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; +use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs; +use Doctrine\ODM\MongoDB\PersistentCollection; +use Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\NotifyPropertyChanged; +use Doctrine\Common\PropertyChangedListener; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\MongoDB\GridFSFile; +use Doctrine\ODM\MongoDB\Query\Query; +use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory; + +/** + * The UnitOfWork is responsible for tracking changes to objects during an + * "object-level" transaction and for writing out changes to the database + * in the correct order. + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class UnitOfWork implements PropertyChangedListener +{ + /** + * An document is in MANAGED state when its persistence is managed by an DocumentManager. + */ + const STATE_MANAGED = 1; + + /** + * An document is new if it has just been instantiated (i.e. using the "new" operator) + * and is not (yet) managed by an DocumentManager. + */ + const STATE_NEW = 2; + + /** + * A detached document is an instance with a persistent identity that is not + * (or no longer) associated with an DocumentManager (and a UnitOfWork). + */ + const STATE_DETACHED = 3; + + /** + * A removed document instance is an instance with a persistent identity, + * associated with an DocumentManager, whose persistent state has been + * deleted (or is scheduled for deletion). + */ + const STATE_REMOVED = 4; + + /** + * The identity map that holds references to all managed documents that have + * an identity. The documents are grouped by their class name. + * Since all classes in a hierarchy must share the same identifier set, + * we always take the root class name of the hierarchy. + * + * @var array + */ + private $identityMap = array(); + + /** + * Map of all identifiers of managed documents. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $documentIdentifiers = array(); + + /** + * Map of the original document data of managed documents. + * Keys are object ids (spl_object_hash). This is used for calculating changesets + * at commit time. + * + * @var array + * @internal Note that PHPs "copy-on-write" behavior helps a lot with memory usage. + * A value will only really be copied if the value in the document is modified + * by the user. + */ + private $originalDocumentData = array(); + + /** + * Map of document changes. Keys are object ids (spl_object_hash). + * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. + * + * @var array + */ + private $documentChangeSets = array(); + + /** + * The (cached) states of any known documents. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $documentStates = array(); + + /** + * Map of documents that are scheduled for dirty checking at commit time. + * This is only used for documents with a change tracking policy of DEFERRED_EXPLICIT. + * Keys are object ids (spl_object_hash). + * + * @var array + * @todo rename: scheduledForSynchronization + */ + private $scheduledForDirtyCheck = array(); + + /** + * A list of all pending document insertions. + * + * @var array + */ + private $documentInsertions = array(); + + /** + * A list of all pending document updates. + * + * @var array + */ + private $documentUpdates = array(); + + /** + * A list of all pending document upserts. + * + * @var array + */ + private $documentUpserts = array(); + + /** + * Any pending extra updates that have been scheduled by persisters. + * + * @var array + */ + private $extraUpdates = array(); + + /** + * A list of all pending document deletions. + * + * @var array + */ + private $documentDeletions = array(); + + /** + * All pending collection deletions. + * + * @var array + */ + private $collectionDeletions = array(); + + /** + * All pending collection updates. + * + * @var array + */ + private $collectionUpdates = array(); + + /** + * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. + * At the end of the UnitOfWork all these collections will make new snapshots + * of their data. + * + * @var array + */ + private $visitedCollections = array(); + + /** + * The DocumentManager that "owns" this UnitOfWork instance. + * + * @var Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * The calculator used to calculate the order in which changes to + * documents need to be written to the database. + * + * @var Doctrine\ODM\MongoDB\Internal\CommitOrderCalculator + */ + private $commitOrderCalculator; + + /** + * The EventManager used for dispatching events. + * + * @var EventManager + */ + private $evm; + + /** + * Embedded documents that are scheduled for removal. + * + * @var array + */ + private $orphanRemovals = array(); + + /** + * The HydratorFactory used for hydrating array Mongo documents to Doctrine object documents. + * + * @var HydratorFactory + */ + private $hydratorFactory; + + /** + * The document persister instances used to persist document instances. + * + * @var array + */ + private $persisters = array(); + + /** + * The collection persister instance used to persist changes to collections. + * + * @var CollectionPersister + */ + private $collectionPersister; + + /** + * The persistence builder instance used in DocumentPersisters. + * + * @var PersistenceBuilder + */ + private $persistenceBuilder; + + /** + * Array of parent associations between embedded documents + * + * @todo We might need to clean up this array in clear(), doDetach(), etc. + * @var array + */ + private $parentAssociations = array(); + + /** + * Mongo command character + * + * @var string + */ + private $cmd; + + /** + * Initializes a new UnitOfWork instance, bound to the given DocumentManager. + * + * @param Doctrine\ODM\MongoDB\DocumentManager $dm + * @param Doctrine\Common\EventManager $evm + * @param Doctrine\ODM\MongoDB\Hydrator\HydratorFactory $hydratorFactory + * @param string $cmd + */ + public function __construct(DocumentManager $dm, EventManager $evm, HydratorFactory $hydratorFactory, $cmd) + { + $this->dm = $dm; + $this->evm = $evm; + $this->hydratorFactory = $hydratorFactory; + $this->cmd = $cmd; + } + + /** + * Factory for returning new PersistenceBuilder instances used for preparing data into + * queries for insert persistence. + * + * @return PersistenceBuilder $pb + */ + public function getPersistenceBuilder() + { + if (!$this->persistenceBuilder) { + $this->persistenceBuilder = new PersistenceBuilder($this->dm, $this, $this->cmd); + } + return $this->persistenceBuilder; + } + + /** + * Sets the parent association for a given embedded document. + * + * @param object $document + * @param array $mapping + * @param object $parent + * @param string $propertyPath + */ + public function setParentAssociation($document, $mapping, $parent, $propertyPath) + { + $oid = spl_object_hash($document); + $this->parentAssociations[$oid] = array($mapping, $parent, $propertyPath); + } + + /** + * Gets the parent association for a given embedded document. + * + * + * list($mapping, $parent, $propertyPath) = $this->getParentAssociation($embeddedDocument); + * + * + * @param object $document + * @return array $association + */ + public function getParentAssociation($document) + { + $oid = spl_object_hash($document); + if ( ! isset($this->parentAssociations[$oid])) { + return null; + } + return $this->parentAssociations[$oid]; + } + + /** + * Get the document persister instance for the given document name + * + * @param string $documentName + * @return Persisters\DocumentPersister + */ + public function getDocumentPersister($documentName) + { + if ( ! isset($this->persisters[$documentName])) { + $class = $this->dm->getClassMetadata($documentName); + $pb = $this->getPersistenceBuilder(); + $this->persisters[$documentName] = new Persisters\DocumentPersister($pb, $this->dm, $this->evm, $this, $this->hydratorFactory, $class, $this->cmd); + } + return $this->persisters[$documentName]; + } + + /** + * Gets a collection persister for a collection-valued association. + * + * @param array $mapping + * @return Persisters\CollectionPersister + */ + public function getCollectionPersister(array $mapping) + { + if ( ! isset($this->collectionPersister)) { + $pb = $this->getPersistenceBuilder(); + $this->collectionPersister = new Persisters\CollectionPersister($this->dm, $pb, $this, $this->cmd); + } + return $this->collectionPersister; + } + + /** + * Set the document persister instance to use for the given document name + * + * @param string $documentName + * @param Persisters\DocumentPersister $persister + */ + public function setDocumentPersister($documentName, Persisters\DocumentPersister $persister) + { + $this->persisters[$documentName] = $persister; + } + + /** + * Commits the UnitOfWork, executing all operations that have been postponed + * up to this point. The state of all managed documents will be synchronized with + * the database. + * + * The operations are executed in the following order: + * + * 1) All document insertions + * 2) All document updates + * 3) All document deletions + * + * @param object $document + * @param array $options Array of options to be used with batchInsert(), update() and remove() + */ + public function commit($document = null, array $options = array()) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->dm)); + } + + $defaultOptions = $this->dm->getConfiguration()->getDefaultCommitOptions(); + if ($options) { + $options = array_merge($defaultOptions, $options); + } else { + $options = $defaultOptions; + } + // Compute changes done since last commit. + if ($document === null) { + $this->computeChangeSets(); + } else if (is_object($document)) { + $this->computeSingleDocumentChangeSet($document); + } else if (is_array($document)) { + foreach ($document as $object) { + $this->computeSingleDocumentChangeSet($object); + } + } + + if ( ! ($this->documentInsertions || + $this->documentUpserts || + $this->documentDeletions || + $this->documentUpdates || + $this->collectionUpdates || + $this->collectionDeletions || + $this->orphanRemovals)) { + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $removal) { + $this->remove($removal); + } + } + + // Raise onFlush + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->dm)); + } + + // Now we need a commit order to maintain referential integrity + $commitOrder = $this->getCommitOrder(); + + if ($this->documentInsertions) { + foreach ($commitOrder as $class) { + if ($class->isEmbeddedDocument) { + continue; + } + $this->executeInserts($class, $options); + } + } + + if ($this->documentUpdates) { + foreach ($commitOrder as $class) { + $this->executeUpdates($class, $options); + } + } + + // Extra updates that were requested by persisters. + if ($this->extraUpdates) { + $this->executeExtraUpdates($options); + } + + // Collection deletions (deletions of complete collections) + foreach ($this->collectionDeletions as $collectionToDelete) { + $this->getCollectionPersister($collectionToDelete->getMapping()) + ->delete($collectionToDelete, $options); + } + // Collection updates (deleteRows, updateRows, insertRows) + foreach ($this->collectionUpdates as $collectionToUpdate) { + $this->getCollectionPersister($collectionToUpdate->getMapping()) + ->update($collectionToUpdate, $options); + } + + // Document deletions come last and need to be in reverse commit order + if ($this->documentDeletions) { + for ($count = count($commitOrder), $i = $count - 1; $i >= 0; --$i) { + $this->executeDeletions($commitOrder[$i], $options); + } + } + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + // Raise postFlush + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->dm)); + } + + // Clear up + $this->documentInsertions = + $this->documentUpserts = + $this->documentUpdates = + $this->documentDeletions = + $this->extraUpdates = + $this->documentChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForDirtyCheck = + $this->orphanRemovals = array(); + } + + /** + * Compute the changesets of all documents scheduled for insertion + * + * @return void + */ + private function computeScheduleInsertsChangeSets() + { + foreach ($this->documentInsertions as $document) { + $class = $this->dm->getClassMetadata(get_class($document)); + + $this->computeChangeSet($class, $document); + } + } + + /** + * Only flush the given document according to a ruleset that keeps the UoW consistent. + * + * 1. All documents scheduled for insertion, (orphan) removals and changes in collections are processed as well! + * 2. Proxies are skipped. + * 3. Only if document is properly managed. + * + * @param object $document + * @return void + */ + private function computeSingleDocumentChangeSet($document) + { + if ($this->getDocumentState($document) !== self::STATE_MANAGED) { + throw new \InvalidArgumentException("Document has to be managed for single computation " . self::objToStr($document)); + } + + $class = $this->dm->getClassMetadata(get_class($document)); + + if ($class->isChangeTrackingDeferredImplicit()) { + $this->persist($document); + } + + // Compute changes for INSERTed documents first. This must always happen even in this case. + $this->computeScheduleInsertsChangeSets(); + + // Ignore uninitialized proxy objects + if ($document instanceof Proxy && ! $document->__isInitialized__) { + return; + } + + // Only MANAGED documents that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($document); + + if ( ! isset($this->documentInsertions[$oid]) && isset($this->documentStates[$oid])) { + $this->computeChangeSet($class, $document); + } + } + + /** + * Executes reference updates + */ + private function executeExtraUpdates(array $options) + { + foreach ($this->extraUpdates as $oid => $update) { + list ($document, $changeset) = $update; + $this->documentChangeSets[$oid] = $changeset; + $this->getDocumentPersister(get_class($document))->update($document, $options); + } + } + + /** + * Gets the changeset for an document. + * + * @return array + */ + public function getDocumentChangeSet($document) + { + $oid = spl_object_hash($document); + if (isset($this->documentChangeSets[$oid])) { + return $this->documentChangeSets[$oid]; + } + return array(); + } + + /** + * Get a documents actual data, flattening all the objects to arrays. + * + * @param object $document + * @return array + */ + public function getDocumentActualData($document) + { + $class = $this->dm->getClassMetadata(get_class($document)); + $actualData = array(); + foreach ($class->reflFields as $name => $refProp) { + $mapping = $class->fieldMappings[$name]; + // skip not saved fields + if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { + continue; + } + $value = $refProp->getValue($document); + if (isset($mapping['file']) && ! $value instanceof GridFSFile) { + $value = new GridFSFile($value); + $class->reflFields[$name]->setValue($document, $value); + $actualData[$name] = $value; + } elseif ((isset($mapping['association']) && $mapping['type'] === 'many') + && $value !== null && ! ($value instanceof PersistentCollection)) { + // If $actualData[$name] is not a Collection then use an ArrayCollection. + if ( ! $value instanceof Collection) { + $value = new ArrayCollection($value); + } + + // Inject PersistentCollection + $coll = new PersistentCollection($value, $this->dm, $this, $this->cmd); + $coll->setOwner($document, $mapping); + $coll->setDirty( ! $value->isEmpty()); + $class->reflFields[$name]->setValue($document, $coll); + $actualData[$name] = $coll; + } else { + $actualData[$name] = $value; + } + } + return $actualData; + } + + /** + * Computes the changes that happened to a single document. + * + * Modifies/populates the following properties: + * + * {@link originalDocumentData} + * If the document is NEW or MANAGED but not yet fully persisted (only has an id) + * then it was not fetched from the database and therefore we have no original + * document data yet. All of the current document data is stored as the original document data. + * + * {@link documentChangeSets} + * The changes detected on all properties of the document are stored there. + * A change is a tuple array where the first entry is the old value and the second + * entry is the new value of the property. Changesets are used by persisters + * to INSERT/UPDATE the persistent document state. + * + * {@link documentUpdates} + * If the document is already fully MANAGED (has been fetched from the database before) + * and any changes to its properties are detected, then a reference to the document is stored + * there to mark it for an update. + * + * @param ClassMetadata $class The class descriptor of the document. + * @param object $document The document for which to compute the changes. + */ + public function computeChangeSet(ClassMetadata $class, $document) + { + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->dm->getClassMetadata(get_class($document)); + } + + // Fire PreFlush lifecycle callbacks + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + $class->invokeLifecycleCallbacks(Events::preFlush, $document); + } + + $this->computeOrRecomputeChangeSet($class, $document); + } + + /** + * Used to do the common work of computeChangeSet and recomputeSingleDocumentChangeSet + * + * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class + * @param object $document + * @param boolean $recompute + */ + private function computeOrRecomputeChangeSet(ClassMetadata $class, $document, $recompute = false) + { + $oid = spl_object_hash($document); + $actualData = $this->getDocumentActualData($document); + $isNewDocument = ! isset($this->originalDocumentData[$oid]); + if ($isNewDocument) { + // Document is either NEW or MANAGED but not yet fully persisted (only has an id). + // These result in an INSERT. + $this->originalDocumentData[$oid] = $actualData; + $changeSet = array(); + foreach ($actualData as $propName => $actualValue) { + $changeSet[$propName] = array(null, $actualValue); + } + $this->documentChangeSets[$oid] = $changeSet; + } else { + // Document is "fully" MANAGED: it was already fully persisted before + // and we have a copy of the original data + $originalData = $this->originalDocumentData[$oid]; + $isChangeTrackingNotify = $class->isChangeTrackingNotify(); + if ($isChangeTrackingNotify && !$recompute) { + $changeSet = $this->documentChangeSets[$oid]; + } else { + $changeSet = array(); + } + + foreach ($actualData as $propName => $actualValue) { + // skip not saved fields + if (isset($class->fieldMappings[$propName]['notSaved']) && $class->fieldMappings[$propName]['notSaved'] === true) { + continue; + } + + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + // skip if value has not changed + if ($orgValue === $actualValue) { + // but consider dirty GridFSFile instances as changed + if ( ! (isset($class->fieldMappings[$propName]['file']) && $actualValue->isDirty())) { + continue; + } + } + + // if embed-one relationship + if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one') { + if ($orgValue !== null) { + $this->scheduleOrphanRemoval($orgValue); + } + $changeSet[$propName] = array($orgValue, $actualValue); + continue; + } + + // if owning side of reference-one relationship + if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $class->fieldMappings[$propName]['isOwningSide']) { + $changeSet[$propName] = array($orgValue, $actualValue); + continue; + } + + if ($isChangeTrackingNotify) { + continue; + } + + // ignore inverse side of reference-many relationship + if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'many' && $class->fieldMappings[$propName]['isInverseSide']) { + continue; + } + + // Persistent collection was exchanged with the "originally" + // created one. This can only mean it was cloned and replaced + // on another document. + if ($actualValue instanceof PersistentCollection) { + $owner = $actualValue->getOwner(); + if ($owner === null) { // cloned + $actualValue->setOwner($document, $class->fieldMappings[$propName]); + } else if ($owner !== $document) { // no clone, we have to fix + if (!$actualValue->isInitialized()) { + $actualValue->initialize(); // we have to do this otherwise the cols share state + } + $newValue = clone $actualValue; + $newValue->setOwner($document, $class->fieldMappings[$propName]); + $class->reflFields[$propName]->setValue($document, $newValue); + } + } + + // if embed-many or reference-many relationship + if ($class->fieldMappings[$propName]['type'] === 'many') { + $changeSet[$propName] = array($orgValue, $actualValue); + if ($orgValue instanceof PersistentCollection) { + $this->collectionDeletions[] = $orgValue; + } + continue; + } + + // skip equivalent date values + if ($class->fieldMappings[$propName]['type'] === 'date') { + $dateType = Type::getType('date'); + $dbOrgValue = $dateType->convertToDatabaseValue($orgValue); + $dbActualValue = $dateType->convertToDatabaseValue($actualValue); + + if ($dbOrgValue instanceof \MongoDate && $dbActualValue instanceof \MongoDate && $dbOrgValue == $dbActualValue) { + continue; + } + } + + // regular field + $changeSet[$propName] = array($orgValue, $actualValue); + } + if ($changeSet) { + if ($recompute) { + $this->documentChangeSets[$oid] = $changeSet + $this->documentChangeSets[$oid]; + } else { + $this->documentChangeSets[$oid] = $changeSet; + } + $this->originalDocumentData[$oid] = $actualData; + $this->documentUpdates[$oid] = $document; + } + } + + // Look for changes in associations of the document + foreach ($class->fieldMappings as $mapping) { + // skip not saved fields + if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { + continue; + } + if (isset($mapping['reference']) || isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value !== null) { + $this->computeAssociationChanges($document, $mapping, $value); + if(isset($mapping['reference'])) { + continue; + } + + $values = $value; + if (isset($mapping['type']) && $mapping['type'] === 'one') { + $values = array($values); + } elseif ($values instanceof PersistentCollection) { + $values = $values->unwrap(); + } + foreach ($values as $obj) { + $oid2 = spl_object_hash($obj); + if (isset($this->documentChangeSets[$oid2])) { + $this->documentChangeSets[$oid][$mapping['fieldName']] = array($value, $value); + if (!$isNewDocument) { + $this->documentUpdates[$oid] = $document; + } + break; + } + } + } + } + } + } + + /** + * Computes all the changes that have been done to documents and collections + * since the last commit and stores these changes in the _documentChangeSet map + * temporarily for access by the persisters, until the UoW commit is finished. + */ + public function computeChangeSets() + { + $this->computeScheduleInsertsChangeSets(); + + // Compute changes for other MANAGED documents. Change tracking policies take effect here. + foreach ($this->identityMap as $className => $documents) { + $class = $this->dm->getClassMetadata($className); + if($class->isEmbeddedDocument) { + // Embedded documents should only compute by the document itself which include the embedded document. + // This is done separately later. + // @see computeChangeSet() + // @see computeAssociationChanges() + continue; + } + + // If change tracking is explicit or happens through notification, then only compute + // changes on documents of that type that are explicitly marked for synchronization. + $documentsToProcess = ! $class->isChangeTrackingDeferredImplicit() ? + (isset($this->scheduledForDirtyCheck[$className]) ? + $this->scheduledForDirtyCheck[$className] : array()) + : $documents; + + foreach ($documentsToProcess as $document) { + // Ignore uninitialized proxy objects + if (/* $document is readOnly || */ $document instanceof Proxy && ! $document->__isInitialized__) { + continue; + } + // Only MANAGED documents that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($document); + if ( ! isset($this->documentInsertions[$oid]) && isset($this->documentStates[$oid])) { + $this->computeChangeSet($class, $document); + } + } + } + } + + /** + * Computes the changes of an embedded document. + * + * @param object $parentDocument + * @param array $mapping + * @param mixed $value The value of the association. + */ + private function computeAssociationChanges($parentDocument, $mapping, $value) + { + $isNewParentDocument = isset($this->documentInsertions[spl_object_hash($parentDocument)]); + $class = $this->dm->getClassMetadata(get_class($parentDocument)); + $topOrExistingDocument = (!$isNewParentDocument || !$class->isEmbeddedDocument); + + if ($value instanceof PersistentCollection && $value->isDirty() && $mapping['isOwningSide'] && ($topOrExistingDocument || $mapping['strategy'] === 'set')) { + if (!in_array($value, $this->collectionUpdates, true)) { + $this->collectionUpdates[] = $value; + } + $this->visitedCollections[] = $value; + } else if ($value instanceof PersistentCollection && $value->isDirty() && $mapping['isOwningSide']) { + $this->visitedCollections[] = $value; + } + + if ( ! isset($mapping['embedded']) && ! $mapping['isCascadePersist']) { + return; // "Persistence by reachability" only if persist cascade specified + } + + if ($mapping['type'] === 'one') { + if ($value instanceof Proxy && ! $value->__isInitialized__) { + return; // Ignore uninitialized proxy objects + } + $value = array($value); + } elseif ($value instanceof PersistentCollection) { + $value = $value->unwrap(); + } + $count = 0; + foreach ($value as $key => $entry) { + $targetClass = $this->dm->getClassMetadata(get_class($entry)); + $state = $this->getDocumentState($entry, self::STATE_NEW); + $oid = spl_object_hash($entry); + + // Handle "set" strategy for multi-level hierarchy + $pathKey = $mapping['strategy'] !== 'set' ? $count : $key; + $path = $mapping['type'] === 'many' ? $mapping['name'].'.'.$pathKey : $mapping['name']; + + $count++; + if ($state == self::STATE_NEW) { + if ( ! $targetClass->isEmbeddedDocument && ! $mapping['isCascadePersist']) { + throw new \InvalidArgumentException("A new document was found through a relationship that was not" + . " configured to cascade persist operations: " . self::objToStr($entry) . "." + . " Explicitly persist the new document or configure cascading persist operations" + . " on the relationship."); + } + $this->persistNew($targetClass, $entry); + $this->setParentAssociation($entry, $mapping, $parentDocument, $path); + $this->computeChangeSet($targetClass, $entry); + } else if ($state == self::STATE_MANAGED && $targetClass->isEmbeddedDocument) { + $this->setParentAssociation($entry, $mapping, $parentDocument, $path); + $this->computeChangeSet($targetClass, $entry); + } else if ($state == self::STATE_REMOVED) { + return new \InvalidArgumentException("Removed document detected during flush: " + . self::objToStr($entry).". Remove deleted documents from associations."); + } else if ($state == self::STATE_DETACHED) { + // Can actually not happen right now as we assume STATE_NEW, + // so the exception will be raised from the DBAL layer (constraint violation). + throw new \InvalidArgumentException("A detached document was found through a " + . "relationship during cascading a persist operation."); + } + } + } + + /** + * INTERNAL: + * Computes the changeset of an individual document, independently of the + * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). + * + * The passed document must be a managed document. If the document already has a change set + * because this method is invoked during a commit cycle then the change sets are added. + * whereby changes detected in this method prevail. + * + * @ignore + * @param ClassMetadata $class The class descriptor of the document. + * @param object $document The document for which to (re)calculate the change set. + * @throws InvalidArgumentException If the passed document is not MANAGED. + */ + public function recomputeSingleDocumentChangeSet(ClassMetadata $class, $document) + { + $oid = spl_object_hash($document); + + if ( ! isset($this->documentStates[$oid]) || $this->documentStates[$oid] != self::STATE_MANAGED) { + throw new \InvalidArgumentException('Document must be managed.'); + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->dm->getClassMetadata(get_class($document)); + } + + $this->computeOrRecomputeChangeSet($class, $document, true); + } + + private function persistNew($class, $document) + { + $oid = spl_object_hash($document); + if (isset($class->lifecycleCallbacks[Events::prePersist])) { + $class->invokeLifecycleCallbacks(Events::prePersist, $document); + } + if ($this->evm->hasListeners(Events::prePersist)) { + $this->evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($document, $this->dm)); + } + + $this->documentStates[$oid] = self::STATE_MANAGED; + + $this->scheduleForInsert($class, $document); + } + + /** + * Executes all document insertions for documents of the specified type. + * + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class + * @param array $options Array of options to be used with batchInsert() + */ + private function executeInserts($class, array $options = array()) + { + $className = $class->name; + $persister = $this->getDocumentPersister($className); + $collection = $this->dm->getDocumentCollection($className); + + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]); + $hasListeners = $this->evm->hasListeners(Events::postPersist); + if ($hasLifecycleCallbacks || $hasListeners) { + $documents = array(); + } + + $inserts = array(); + foreach ($this->documentInsertions as $oid => $document) { + if (get_class($document) === $className) { + $persister->addInsert($document); + unset($this->documentInsertions[$oid]); + if ($hasLifecycleCallbacks || $hasListeners) { + $documents[] = $document; + } + } + } + + $postInsertIds = $persister->executeInserts($options); + + if ($postInsertIds) { + foreach ($postInsertIds as $pair) { + list($id, $document) = $pair; + $oid = spl_object_hash($document); + $class->setIdentifierValue($document, $id); + $this->documentIdentifiers[$oid] = $id; + $this->documentStates[$oid] = self::STATE_MANAGED; + $this->originalDocumentData[$oid][$class->identifier] = $id; + $this->addToIdentityMap($document); + + if ($hasLifecycleCallbacks || $hasListeners) { + if ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postPersist, $document); + } + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($document, $this->dm)); + } + } + $this->cascadePostPersist($class, $document); + } + } + } + + /** + * Cascades the postPersist events to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + */ + private function cascadePostPersist(ClassMetadata $class, $document) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + $hasLifecycleCallbacks = isset($entryClass->lifecycleCallbacks[Events::postPersist]); + $hasListeners = $this->evm->hasListeners(Events::postPersist); + if ($hasLifecycleCallbacks || $hasListeners) { + if ($hasLifecycleCallbacks) { + $entryClass->invokeLifecycleCallbacks(Events::postPersist, $entry); + } + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entry, $this->dm)); + } + } + $this->cascadePostPersist($entryClass, $entry); + } + } + } + } + + /** + * Executes all document updates for documents of the specified type. + * + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class + * @param array $options Array of options to be used with update() + */ + private function executeUpdates(ClassMetadata $class, array $options = array()) + { + $className = $class->name; + $persister = $this->getDocumentPersister($className); + + $hasPreUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preUpdate]); + $hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate); + $hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]); + $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate); + + foreach ($this->documentUpdates as $oid => $document) { + if (get_class($document) == $className || $document instanceof Proxy && $document instanceof $className) { + if ( ! $class->isEmbeddedDocument) { + if ($hasPreUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::preUpdate, $document); + $this->recomputeSingleDocumentChangeSet($class, $document); + } + + if ($hasPreUpdateListeners && isset($this->documentChangeSets[$oid])) { + $this->evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs( + $document, $this->dm, $this->documentChangeSets[$oid]) + ); + } + $this->cascadePreUpdate($class, $document); + } + + if ( ! $class->isEmbeddedDocument && isset($this->documentChangeSets[$oid]) && $this->documentChangeSets[$oid]) { + $persister->update($document, $options); + } + unset($this->documentUpdates[$oid]); + + if ( ! $class->isEmbeddedDocument) { + if ($hasPostUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postUpdate, $document); + } + if ($hasPostUpdateListeners) { + $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($document, $this->dm)); + } + $this->cascadePostUpdateAndPostPersist($class, $document); + } + } + } + } + + /** + * Cascades the preUpdate event to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + */ + private function cascadePreUpdate(ClassMetadata $class, $document) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryOid = spl_object_hash($entry); + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + if ( ! isset($this->documentChangeSets[$entryOid])) { + continue; + } + if ( ! isset($this->documentInsertions[$entryOid])) { + if (isset($entryClass->lifecycleCallbacks[Events::preUpdate])) { + $entryClass->invokeLifecycleCallbacks(Events::preUpdate, $entry); + $this->recomputeSingleDocumentChangeSet($entryClass, $entry); + } + if ($this->evm->hasListeners(Events::preUpdate)) { + $this->evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs( + $entry, $this->dm, $this->documentChangeSets[$entryOid]) + ); + } + } + $this->cascadePreUpdate($entryClass, $entry); + } + } + } + } + + /** + * Cascades the postUpdate and postPersist events to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + */ + private function cascadePostUpdateAndPostPersist(ClassMetadata $class, $document) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryOid = spl_object_hash($entry); + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + if ( ! isset($this->documentChangeSets[$entryOid])) { + continue; + } + if (isset($this->documentInsertions[$entryOid])) { + if (isset($entryClass->lifecycleCallbacks[Events::postPersist])) { + $entryClass->invokeLifecycleCallbacks(Events::postPersist, $entry); + } + if ($this->evm->hasListeners(Events::postPersist)) { + $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entry, $this->dm)); + } + } else { + if (isset($entryClass->lifecycleCallbacks[Events::postUpdate])) { + $entryClass->invokeLifecycleCallbacks(Events::postUpdate, $entry); + $this->recomputeSingleDocumentChangeSet($entryClass, $entry); + } + if ($this->evm->hasListeners(Events::postUpdate)) { + $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($entry, $this->dm)); + } + } + $this->cascadePostUpdateAndPostPersist($entryClass, $entry); + } + } + } + } + + /** + * Executes all document deletions for documents of the specified type. + * + * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class + * @param array $options Array of options to be used with remove() + */ + private function executeDeletions(ClassMetadata $class, array $options = array()) + { + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]); + $hasListeners = $this->evm->hasListeners(Events::postRemove); + + $className = $class->name; + $persister = $this->getDocumentPersister($className); + $collection = $this->dm->getDocumentCollection($className); + foreach ($this->documentDeletions as $oid => $document) { + if (get_class($document) == $className || $document instanceof Proxy && $document instanceof $className) { + if ( ! $class->isEmbeddedDocument) { + $persister->delete($document, $options); + } + unset( + $this->documentDeletions[$oid], + $this->documentIdentifiers[$oid], + $this->originalDocumentData[$oid] + ); + + // Clear snapshot information for any referenced PersistentCollection + // http://www.doctrine-project.org/jira/browse/MODM-95 + foreach ($class->fieldMappings as $fieldMapping) { + if (isset($fieldMapping['type']) && $fieldMapping['type'] === 'many') { + $value = $class->reflFields[$fieldMapping['fieldName']]->getValue($document); + if ($value instanceof PersistentCollection) { + $value->clearSnapshot(); + } + } + } + + // Document with this $oid after deletion treated as NEW, even if the $oid + // is obtained by a new document because the old one went out of scope. + $this->documentStates[$oid] = self::STATE_NEW; + + if ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postRemove, $document); + } + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($document, $this->dm)); + } + $this->cascadePostRemove($class, $document); + } + } + } + + /** + * Gets the commit order. + * + * @return array + */ + private function getCommitOrder(array $documentChangeSet = null) + { + if ($documentChangeSet === null) { + $documentChangeSet = array_merge( + $this->documentInsertions, + $this->documentUpserts, + $this->documentUpdates, + $this->documentDeletions + ); + } + + $calc = $this->getCommitOrderCalculator(); + + // See if there are any new classes in the changeset, that are not in the + // commit order graph yet (don't have a node). + // We have to inspect changeSet to be able to correctly build dependencies. + // It is not possible to use IdentityMap here because post inserted ids + // are not yet available. + $newNodes = array(); + + foreach ($documentChangeSet as $oid => $document) { + $className = get_class($document); + + if ($calc->hasClass($className)) { + continue; + } + + $class = $this->dm->getClassMetadata($className); + $calc->addClass($class); + + $newNodes[] = $class; + } + + // Calculate dependencies for new nodes + while ($class = array_pop($newNodes)) { + $this->addDependencies($class, $calc); + } + return $calc->getCommitOrder(); + } + + /** + * Add dependencies recursively through embedded documents. Embedded documents + * may have references to other documents so those need to be saved first. + * + * @param ClassMetadata $class + * @param CommitOrderCalculator $calc + */ + private function addDependencies(ClassMetadata $class, $calc) + { + foreach ($class->fieldMappings as $mapping) { + $isOwningReference = isset($mapping['reference']) && $mapping['isOwningSide']; + $isAssociation = isset($mapping['embedded']) || $isOwningReference; + if (!$isAssociation || !isset($mapping['targetDocument'])) { + continue; + } + + $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); + + if ( ! $calc->hasClass($targetClass->name)) { + $calc->addClass($targetClass); + } + + $calc->addDependency($targetClass, $class); + + // If the target class has mapped subclasses, these share the same dependency. + if ( ! $targetClass->subClasses) { + continue; + } + + foreach ($targetClass->subClasses as $subClassName) { + $targetSubClass = $this->dm->getClassMetadata($subClassName); + + if ( ! $calc->hasClass($subClassName)) { + $calc->addClass($targetSubClass); + + $newNodes[] = $targetSubClass; + } + + $calc->addDependency($targetSubClass, $class); + } + + // avoid infinite recursion + if ($class !== $targetClass) { + $this->addDependencies($targetClass, $calc); + } + } + } + + /** + * Schedules an document for insertion into the database. + * If the document already has an identifier, it will be added to the identity map. + * + * @param object $document The document to schedule for insertion. + */ + public function scheduleForInsert($class, $document) + { + $oid = spl_object_hash($document); + + if (isset($this->documentUpdates[$oid])) { + throw new \InvalidArgumentException("Dirty document can not be scheduled for insertion."); + } + if (isset($this->documentDeletions[$oid])) { + throw new \InvalidArgumentException("Removed document can not be scheduled for insertion."); + } + if (isset($this->documentInsertions[$oid])) { + throw new \InvalidArgumentException("Document can not be scheduled for insertion twice."); + } + + $this->documentInsertions[$oid] = $document; + + if (!$class->isEmbeddedDocument && $idValue = $class->getIdentifierValue($document)) { + $this->documentUpserts[$oid] = $document; + $this->documentIdentifiers[$oid] = $idValue; + } + + if (isset($this->documentIdentifiers[$oid])) { + $this->addToIdentityMap($document); + } + } + + /** + * Checks whether an document is scheduled for insertion. + * + * @param object $document + * @return boolean + */ + public function isScheduledForInsert($document) + { + return isset($this->documentInsertions[spl_object_hash($document)]); + } + + /** + * Checks whether an document is scheduled for upsert. + * + * @param object $document + * @return boolean + */ + public function isScheduledForUpsert($document) + { + return isset($this->documentUpserts[spl_object_hash($document)]); + } + + /** + * Schedules an document for being updated. + * + * @param object $document The document to schedule for being updated. + */ + public function scheduleForUpdate($document) + { + $oid = spl_object_hash($document); + if ( ! isset($this->documentIdentifiers[$oid])) { + throw new \InvalidArgumentException("Document has no identity."); + } + if (isset($this->documentDeletions[$oid])) { + throw new \InvalidArgumentException("Document is removed."); + } + + if ( ! isset($this->documentUpdates[$oid]) && ! isset($this->documentInsertions[$oid])) { + $this->documentUpdates[$oid] = $document; + } + } + + /** + * INTERNAL: + * Schedules an extra update that will be executed immediately after the + * regular entity updates within the currently running commit cycle. + * + * Extra updates for documents are stored as (entity, changeset) tuples. + * + * @ignore + * @param object $document The entity for which to schedule an extra update. + * @param array $changeset The changeset of the entity (what to update). + */ + public function scheduleExtraUpdate($document, array $changeset) + { + $oid = spl_object_hash($document); + if (isset($this->extraUpdates[$oid])) { + list($ignored, $changeset2) = $this->extraUpdates[$oid]; + $this->extraUpdates[$oid] = array($document, $changeset + $changeset2); + } else { + $this->extraUpdates[$oid] = array($document, $changeset); + } + } + + /** + * Checks whether an document is registered as dirty in the unit of work. + * Note: Is not very useful currently as dirty documents are only registered + * at commit time. + * + * @param object $document + * @return boolean + */ + public function isScheduledForUpdate($document) + { + return isset($this->documentUpdates[spl_object_hash($document)]); + } + + public function isScheduledForDirtyCheck($document) + { + $rootDocumentName = $this->dm->getClassMetadata(get_class($document))->rootDocumentName; + return isset($this->scheduledForDirtyCheck[$rootDocumentName][spl_object_hash($document)]); + } + + /** + * INTERNAL: + * Schedules an document for deletion. + * + * @param object $document + */ + public function scheduleForDelete($document) + { + $oid = spl_object_hash($document); + + if (isset($this->documentInsertions[$oid])) { + if ($this->isInIdentityMap($document)) { + $this->removeFromIdentityMap($document); + } + unset($this->documentInsertions[$oid]); + return; // document has not been persisted yet, so nothing more to do. + } + + if ( ! $this->isInIdentityMap($document)) { + return; // ignore + } + + $this->removeFromIdentityMap($document); + $this->documentStates[$oid] = self::STATE_REMOVED; + + if (isset($this->documentUpdates[$oid])) { + unset($this->documentUpdates[$oid]); + } + if ( ! isset($this->documentDeletions[$oid])) { + $this->documentDeletions[$oid] = $document; + } + } + + /** + * Checks whether an document is registered as removed/deleted with the unit + * of work. + * + * @param object $document + * @return boolean + */ + public function isScheduledForDelete($document) + { + return isset($this->documentDeletions[spl_object_hash($document)]); + } + + /** + * Checks whether an document is scheduled for insertion, update or deletion. + * + * @param $document + * @return boolean + */ + public function isDocumentScheduled($document) + { + $oid = spl_object_hash($document); + return isset($this->documentInsertions[$oid]) || + isset($this->documentUpdates[$oid]) || + isset($this->documentDeletions[$oid]); + } + + /** + * INTERNAL: + * Registers an document in the identity map. + * Note that documents in a hierarchy are registered with the class name of + * the root document. + * + * @ignore + * @param object $document The document to register. + * @return boolean TRUE if the registration was successful, FALSE if the identity of + * the document in question is already managed. + */ + public function addToIdentityMap($document) + { + $classMetadata = $this->dm->getClassMetadata(get_class($document)); + if ($classMetadata->isEmbeddedDocument) { + $id = spl_object_hash($document); + } else { + $id = $this->documentIdentifiers[spl_object_hash($document)]; + $id = $classMetadata->getPHPIdentifierValue($id); + } + if ($id === '') { + throw new \InvalidArgumentException("The given document has no identity."); + } + $className = $classMetadata->rootDocumentName; + if (isset($this->identityMap[$className][$id])) { + return false; + } + $this->identityMap[$className][$id] = $document; + if ($document instanceof NotifyPropertyChanged) { + $document->addPropertyChangedListener($this); + } + return true; + } + + /** + * Gets the state of an document within the current unit of work. + * + * NOTE: This method sees documents that are not MANAGED or REMOVED and have a + * populated identifier, whether it is generated or manually assigned, as + * DETACHED. This can be incorrect for manually assigned identifiers. + * + * @param object $document + * @param integer $assume The state to assume if the state is not yet known. This is usually + * used to avoid costly state lookups, in the worst case with a database + * lookup. + * @return int The document state. + */ + public function getDocumentState($document, $assume = null) + { + $oid = spl_object_hash($document); + if ( ! isset($this->documentStates[$oid])) { + $class = $this->dm->getClassMetadata(get_class($document)); + if ($class->isEmbeddedDocument) { + return self::STATE_NEW; + } + // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known. + // Note that you can not remember the NEW or DETACHED state in _documentStates since + // the UoW does not hold references to such objects and the object hash can be reused. + // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it. + if ($assume === null) { + $id = $class->getIdentifierValue($document); + if ( ! $id) { + return self::STATE_NEW; + } else { + // Check for a version field, if available, to avoid a db lookup. + if ($class->isVersioned) { + if ($class->reflFields[$class->versionField]->getValue($document)) { + return self::STATE_DETACHED; + } else { + return self::STATE_NEW; + } + } else { + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootDocumentName)) { + return self::STATE_DETACHED; + } else { + // db lookup + if ($this->getDocumentPersister(get_class($document))->exists($document)) { + return self::STATE_DETACHED; + } else { + return self::STATE_NEW; + } + } + } + } + } else { + return $assume; + } + } + return $this->documentStates[$oid]; + } + + /** + * INTERNAL: + * Removes an document from the identity map. This effectively detaches the + * document from the persistence management of Doctrine. + * + * @ignore + * @param object $document + * @return boolean + */ + public function removeFromIdentityMap($document) + { + $oid = spl_object_hash($document); + $classMetadata = $this->dm->getClassMetadata(get_class($document)); + + // Check if id is registered first + if (!isset($this->documentIdentifiers[$oid])) { + return false; + } + $id = $this->documentIdentifiers[$oid]; + + if ( ! $classMetadata->isEmbeddedDocument) { + $id = $classMetadata->getPHPIdentifierValue($id); + } + if ($id === '') { + throw new \InvalidArgumentException("The given document has no identity."); + } + $className = $classMetadata->rootDocumentName; + if (isset($this->identityMap[$className][$id])) { + unset($this->identityMap[$className][$id]); + $this->documentStates[$oid] = self::STATE_DETACHED; + return true; + } + + return false; + } + + /** + * INTERNAL: + * Gets an document in the identity map by its identifier hash. + * + * @ignore + * @param string $id + * @param string $rootClassName + * @return object + */ + public function getById($id, $rootClassName) + { + return $this->identityMap[$rootClassName][$id]; + } + + /** + * INTERNAL: + * Tries to get an document by its identifier hash. If no document is found for + * the given hash, FALSE is returned. + * + * @ignore + * @param string $id + * @param string $rootClassName + * @return mixed The found document or FALSE. + */ + public function tryGetById($id, $rootClassName) + { + return isset($this->identityMap[$rootClassName][$id]) ? + $this->identityMap[$rootClassName][$id] : false; + } + + /** + * Schedules a document for dirty-checking at commit-time. + * + * @param object $document The document to schedule for dirty-checking. + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($document) + { + $rootClassName = $this->dm->getClassMetadata(get_class($document))->rootDocumentName; + $this->scheduledForDirtyCheck[$rootClassName][spl_object_hash($document)] = $document; + } + + /** + * Checks whether an document is registered in the identity map of this UnitOfWork. + * + * @param object $document + * @return boolean + */ + public function isInIdentityMap($document) + { + $oid = spl_object_hash($document); + if ( ! isset($this->documentIdentifiers[$oid])) { + return false; + } + $classMetadata = $this->dm->getClassMetadata(get_class($document)); + $id = $this->documentIdentifiers[$oid]; + if ( ! $classMetadata->isEmbeddedDocument) { + $id = $classMetadata->getPHPIdentifierValue($id); + } + if ($id === '') { + return false; + } + + return isset($this->identityMap[$classMetadata->rootDocumentName][$id]); + } + + /** + * INTERNAL: + * Checks whether an identifier hash exists in the identity map. + * + * @ignore + * @param string $id + * @param string $rootClassName + * @return boolean + */ + public function containsId($id, $rootClassName) + { + return isset($this->identityMap[$rootClassName][$id]); + } + + /** + * Persists an document as part of the current unit of work. + * + * @param object $document The document to persist. + */ + public function persist($document) + { + $class = $this->dm->getClassMetadata(get_class($document)); + if ($class->isMappedSuperclass) { + throw MongoDBException::cannotPersistMappedSuperclass($class->name); + } + $visited = array(); + $this->doPersist($document, $visited); + } + + /** + * Saves an document as part of the current unit of work. + * This method is internally called during save() cascades as it tracks + * the already visited documents to prevent infinite recursions. + * + * NOTE: This method always considers documents that are not yet known to + * this UnitOfWork as NEW. + * + * @param object $document The document to persist. + * @param array $visited The already visited documents. + */ + private function doPersist($document, array &$visited) + { + $oid = spl_object_hash($document); + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $document; // Mark visited + + $class = $this->dm->getClassMetadata(get_class($document)); + + $documentState = $this->getDocumentState($document, self::STATE_NEW); + switch ($documentState) { + case self::STATE_MANAGED: + // Nothing to do, except if policy is "deferred explicit" + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($document); + } + break; + case self::STATE_NEW: + $this->persistNew($class, $document); + break; + case self::STATE_DETACHED: + throw new \InvalidArgumentException( + "Behavior of persist() for a detached document is not yet defined."); + case self::STATE_REMOVED: + if ( ! $class->isEmbeddedDocument) { + // Document becomes managed again + if ($this->isScheduledForDelete($document)) { + unset($this->documentDeletions[$oid]); + } else { + //FIXME: There's more to think of here... + $this->scheduleForInsert($class, $document); + } + break; + } + default: + throw MongoDBException::invalidDocumentState($documentState); + } + + $this->cascadePersist($document, $visited); + } + + /** + * Deletes an document as part of the current unit of work. + * + * @param object $document The document to remove. + */ + public function remove($document) + { + $visited = array(); + $this->doRemove($document, $visited); + } + + /** + * Deletes an document as part of the current unit of work. + * + * This method is internally called during delete() cascades as it tracks + * the already visited documents to prevent infinite recursions. + * + * @param object $document The document to delete. + * @param array $visited The map of the already visited documents. + * @throws InvalidArgumentException If the instance is a detached document. + */ + private function doRemove($document, array &$visited) + { + $oid = spl_object_hash($document); + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $document; // mark visited + + $class = $this->dm->getClassMetadata(get_class($document)); + $documentState = $this->getDocumentState($document); + switch ($documentState) { + case self::STATE_NEW: + case self::STATE_REMOVED: + // nothing to do + break; + case self::STATE_MANAGED: + if (isset($class->lifecycleCallbacks[Events::preRemove])) { + $class->invokeLifecycleCallbacks(Events::preRemove, $document); + } + if ($this->evm->hasListeners(Events::preRemove)) { + $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($document, $this->dm)); + } + $this->scheduleForDelete($document); + $this->cascadePreRemove($class, $document); + break; + case self::STATE_DETACHED: + throw MongoDBException::detachedDocumentCannotBeRemoved(); + default: + throw MongoDBException::invalidDocumentState($documentState); + } + + $this->cascadeRemove($document, $visited); + } + + /** + * Cascades the preRemove event to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + */ + private function cascadePreRemove(ClassMetadata $class, $document) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + if (isset($entryClass->lifecycleCallbacks[Events::preRemove])) { + $entryClass->invokeLifecycleCallbacks(Events::preRemove, $entry); + } + if ($this->evm->hasListeners(Events::preRemove)) { + $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entry, $this->dm)); + } + $this->cascadePreRemove($entryClass, $entry); + } + } + } + } + + /** + * Cascades the postRemove event to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + */ + private function cascadePostRemove(ClassMetadata $class, $document) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + if (isset($entryClass->lifecycleCallbacks[Events::postRemove])) { + $entryClass->invokeLifecycleCallbacks(Events::postRemove, $entry); + } + if ($this->evm->hasListeners(Events::postRemove)) { + $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($entry, $this->dm)); + } + $this->cascadePostRemove($entryClass, $entry); + } + } + } + } + + /** + * Merges the state of the given detached document into this UnitOfWork. + * + * @param object $document + * @return object The managed copy of the document. + */ + public function merge($document) + { + $visited = array(); + return $this->doMerge($document, $visited); + } + + /** + * Executes a merge operation on an document. + * + * @param object $document + * @param array $visited + * @return object The managed copy of the document. + * @throws InvalidArgumentException If the document instance is NEW. + */ + private function doMerge($document, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($document); + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $document; // mark visited + + $class = $this->dm->getClassMetadata(get_class($document)); + + // First we assume DETACHED, although it can still be NEW but we can avoid + // an extra db-roundtrip this way. If it is not MANAGED but has an identity, + // we need to fetch it from the db anyway in order to merge. + // MANAGED documents are ignored by the merge operation. + if ($this->getDocumentState($document, self::STATE_DETACHED) == self::STATE_MANAGED) { + $managedCopy = $document; + } else { + $id = null; + if (!$class->isEmbeddedDocument) { + // Try to look the entity up in the identity map. + $id = $class->getIdentifierValue($document); + } + + // If there is no ID, it is actually NEW. + if ( ! $id) { + $managedCopy = $class->newInstance(); + $this->persistNew($class, $managedCopy); + } else { + $managedCopy = $this->tryGetById($id, $class->rootDocumentName); + if ($managedCopy) { + // We have the entity in-memory already, just make sure its not removed. + if ($this->getDocumentState($managedCopy) == self::STATE_REMOVED) { + throw new InvalidArgumentException('Removed entity detected during merge.' + . ' Can not merge with a removed entity.'); + } + } else { + // We need to fetch the managed copy in order to merge. + $managedCopy = $this->dm->find($class->name, $id); + } + + if ($managedCopy === null) { + // If the identifier is ASSIGNED, it is NEW, otherwise an error + // since the managed entity was not found. + $managedCopy = $class->newInstance(); + $class->setIdentifierValue($managedCopy, $id); + $this->persistNew($class, $managedCopy); + } + } + + if ($class->isVersioned) { + $managedCopyVersion = $class->reflFields[$class->versionField]->getValue($managedCopy); + $documentVersion = $class->reflFields[$class->versionField]->getValue($document); + // Throw exception if versions don't match. + if ($managedCopyVersion != $documentVersion) { + throw LockException::lockFailedVersionMissmatch($documentVersion, $managedCopyVersion); + } + } + + // Merge state of $document into existing (managed) entity + foreach ($class->reflFields as $name => $prop) { + if ( ! isset($class->fieldMappings[$name]['embedded']) && ! isset($class->fieldMappings[$name]['reference'])) { + $prop->setValue($managedCopy, $prop->getValue($document)); + } else { + $assoc2 = $class->fieldMappings[$name]; + if ($assoc2['type'] === 'one') { + $other = $prop->getValue($document); + if ($other === null) { + $prop->setValue($managedCopy, null); + } else if ($other instanceof Proxy && !$other->__isInitialized__) { + // do not merge fields marked lazy that have not been fetched. + continue; + } else if ( ! isset($assoc2['embedded']) && ! $assoc2['isCascadeMerge']) { + if ($this->getDocumentState($other, self::STATE_DETACHED) == self::STATE_MANAGED) { + $prop->setValue($managedCopy, $other); + } else { + $targetDocument = isset($assoc2['targetDocument']) ? $assoc2['targetDocument'] : get_class($other); + $targetClass = $this->dm->getClassMetadata($targetDocument); + $id = $targetClass->getIdentifierValue($other); + $proxy = $this->dm->getProxyFactory()->getProxy($targetDocument, $id); + $prop->setValue($managedCopy, $proxy); + $this->registerManaged($proxy, $id, array()); + } + } + } else { + $mergeCol = $prop->getValue($document); + if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) { + // do not merge fields marked lazy that have not been fetched. + // keep the lazy persistent collection of the managed copy. + continue; + } + + if (null === $mergeCol) { + // consider unset properties as empty collections + $mergeCol = new ArrayCollection(); + } + + foreach ($mergeCol as $entry) { + $targetDocument = isset($assoc2['targetDocument']) ? $assoc2['targetDocument'] : get_class($entry); + $targetClass = $this->dm->getClassMetadata($targetDocument); + if ($targetClass->isEmbeddedDocument) { + $this->registerManaged($entry, null, array()); + } else { + $id = $targetClass->getIdentifierValue($entry); + $this->registerManaged($entry, $id, array()); + } + } + + if ( ! $mergeCol instanceof PersistentCollection) { + if ( ! $mergeCol instanceof Collection) { + $mergeCol = new ArrayCollection($mergeCol); + } + $mergeCol = new PersistentCollection($mergeCol, $this->dm, $this, $this->cmd); + $mergeCol->setInitialized(true); + } else { + $mergeCol->setDocumentManager($this->dm); + } + $mergeCol->setOwner($managedCopy, $assoc2); + $mergeCol->setDirty(true); // mark for dirty checking + $prop->setValue($managedCopy, $mergeCol); + } + } + if ($class->isChangeTrackingNotify()) { + // Just treat all properties as changed, there is no other choice. + $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); + } + } + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($document); + } + } + + if ($prevManagedCopy !== null) { + $assocField = $assoc->sourceFieldName; + $prevClass = $this->dm->getClassMetadata(get_class($prevManagedCopy)); + if ($assoc->isOneToOne()) { + $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); + } else { + $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->unwrap()->add($managedCopy); + if ($assoc->isOneToMany()) { + $class->reflFields[$assoc->mappedBy]->setValue($managedCopy, $prevManagedCopy); + } + } + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = true; + + $this->cascadeMerge($document, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Detaches an document from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $document The document to detach. + */ + public function detach($document) + { + $visited = array(); + $this->doDetach($document, $visited); + } + + /** + * Executes a detach operation on the given document. + * + * @param object $document + * @param array $visited + * @internal This method always considers documents with an assigned identifier as DETACHED. + */ + private function doDetach($document, array &$visited) + { + $oid = spl_object_hash($document); + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $document; // mark visited + + switch ($this->getDocumentState($document, self::STATE_DETACHED)) { + case self::STATE_MANAGED: + $this->removeFromIdentityMap($document); + unset($this->documentInsertions[$oid], $this->documentUpdates[$oid], + $this->documentDeletions[$oid], $this->documentIdentifiers[$oid], + $this->documentStates[$oid], $this->originalDocumentData[$oid], + $this->parentAssociations[$oid]); + break; + case self::STATE_NEW: + case self::STATE_DETACHED: + return; + } + + $this->cascadeDetach($document, $visited); + } + + /** + * Refreshes the state of the given document from the database, overwriting + * any local, unpersisted changes. + * + * @param object $document The document to refresh. + * @throws InvalidArgumentException If the document is not MANAGED. + */ + public function refresh($document) + { + $visited = array(); + $this->doRefresh($document, $visited); + } + + /** + * Executes a refresh operation on an document. + * + * @param object $document The document to refresh. + * @param array $visited The already visited documents during cascades. + * @throws InvalidArgumentException If the document is not MANAGED. + */ + private function doRefresh($document, array &$visited) + { + $oid = spl_object_hash($document); + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $document; // mark visited + + $class = $this->dm->getClassMetadata(get_class($document)); + if ($this->getDocumentState($document) == self::STATE_MANAGED) { + $id = $class->getDatabaseIdentifierValue($this->documentIdentifiers[$oid]); + $this->getDocumentPersister($class->name)->refresh($id, $document); + } else { + throw new \InvalidArgumentException("Document is not MANAGED."); + } + + $this->cascadeRefresh($document, $visited); + } + + /** + * Cascades a refresh operation to associated documents. + * + * @param object $document + * @param array $visited + */ + private function cascadeRefresh($document, array &$visited) + { + $class = $this->dm->getClassMetadata(get_class($document)); + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['reference']) && ! $mapping['isCascadeRefresh']) { + continue; + } + if (isset($mapping['embedded'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->cascadeRefresh($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->cascadeRefresh($relatedDocuments, $visited); + } + } elseif (isset($mapping['reference'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->doRefresh($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->doRefresh($relatedDocuments, $visited); + } + } + } + } + + /** + * Cascades a detach operation to associated documents. + * + * @param object $document + * @param array $visited + */ + private function cascadeDetach($document, array &$visited) + { + $class = $this->dm->getClassMetadata(get_class($document)); + foreach ($class->fieldMappings as $mapping) { + if ( ! isset($mapping['embedded']) && ! $mapping['isCascadeDetach']) { + continue; + } + if (isset($mapping['embedded'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->cascadeDetach($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->cascadeDetach($relatedDocuments, $visited); + } + } elseif (isset($mapping['reference'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->doDetach($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->doDetach($relatedDocuments, $visited); + } + } + } + } + + /** + * Cascades a merge operation to associated documents. + * + * @param object $document + * @param object $managedCopy + * @param array $visited + */ + private function cascadeMerge($document, $managedCopy, array &$visited) + { + $class = $this->dm->getClassMetadata(get_class($document)); + foreach ($class->fieldMappings as $mapping) { + if ( ! isset($mapping['embedded']) && ! $mapping['isCascadeMerge']) { + continue; + } + if (isset($mapping['embedded']) || isset($mapping['reference'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->doMerge($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->doMerge($relatedDocuments, $visited); + } + } + } + } + + /** + * Cascades the save operation to associated documents. + * + * @param object $document + * @param array $visited + * @param array $insertNow + */ + private function cascadePersist($document, array &$visited) + { + $class = $this->dm->getClassMetadata(get_class($document)); + foreach ($class->fieldMappings as $mapping) { + if ( ! isset($mapping['embedded']) && ! $mapping['isCascadePersist']) { + continue; + } + if (isset($mapping['embedded']) || isset($mapping['reference'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + if ($relatedDocuments instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedDocuments = $relatedDocuments->unwrap(); + } + foreach ($relatedDocuments as $relatedDocument) { + $this->doPersist($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->doPersist($relatedDocuments, $visited); + } + } + } + } + + /** + * Cascades the delete operation to associated documents. + * + * @param object $document + * @param array $visited + */ + private function cascadeRemove($document, array &$visited) + { + $class = $this->dm->getClassMetadata(get_class($document)); + foreach ($class->fieldMappings as $mapping) { + if ( ! isset($mapping['embedded']) && ! $mapping['isCascadeRemove']) { + continue; + } + if (isset($mapping['embedded'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + // If its a PersistentCollection initialization is intended! No unwrap! + foreach ($relatedDocuments as $relatedDocument) { + $this->cascadeRemove($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->cascadeRemove($relatedDocuments, $visited); + } + } elseif (isset($mapping['reference'])) { + $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document); + if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) { + // If its a PersistentCollection initialization is intended! No unwrap! + foreach ($relatedDocuments as $relatedDocument) { + $this->doRemove($relatedDocument, $visited); + } + } elseif ($relatedDocuments !== null) { + $this->doRemove($relatedDocuments, $visited); + } + } + } + } + + /** + * Acquire a lock on the given document. + * + * @param object $document + * @param int $lockMode + * @param int $lockVersion + */ + public function lock($document, $lockMode, $lockVersion = null) + { + if ($this->getDocumentState($document) != self::STATE_MANAGED) { + throw new \InvalidArgumentException("Document is not MANAGED."); + } + + $documentName = get_class($document); + $class = $this->dm->getClassMetadata($documentName); + + if ($lockMode == \Doctrine\ODM\MongoDB\LockMode::OPTIMISTIC) { + if (!$class->isVersioned) { + throw LockException::notVersioned($documentName); + } + + if ($lockVersion != null) { + $documentVersion = $class->reflFields[$class->versionField]->getValue($document); + if ($documentVersion != $lockVersion) { + throw LockException::lockFailedVersionMissmatch($document, $lockVersion, $documentVersion); + } + } + } else if (in_array($lockMode, array(\Doctrine\ODM\MongoDB\LockMode::PESSIMISTIC_READ, \Doctrine\ODM\MongoDB\LockMode::PESSIMISTIC_WRITE))) { + $this->getDocumentPersister($class->name)->lock($document, $lockMode); + } + } + + /** + * Releases a lock on the given document. + * + * @param object $document + */ + public function unlock($document) + { + if ($this->getDocumentState($document) != self::STATE_MANAGED) { + throw new \InvalidArgumentException("Document is not MANAGED."); + } + $documentName = get_class($document); + $this->getDocumentPersister($documentName)->unlock($document); + } + + /** + * Gets the CommitOrderCalculator used by the UnitOfWork to order commits. + * + * @return Doctrine\ODM\MongoDB\Internal\CommitOrderCalculator + */ + public function getCommitOrderCalculator() + { + if ($this->commitOrderCalculator === null) { + $this->commitOrderCalculator = new CommitOrderCalculator; + } + return $this->commitOrderCalculator; + } + + /** + * Clears the UnitOfWork. + * + * @param string|null $documentName if given, only documents of this type will get detached. + */ + public function clear($documentName = null) + { + if ($documentName === null) { + $this->identityMap = + $this->documentIdentifiers = + $this->originalDocumentData = + $this->documentChangeSets = + $this->documentStates = + $this->scheduledForDirtyCheck = + $this->documentInsertions = + $this->documentUpdates = + $this->documentDeletions = + $this->collectionUpdates = + $this->collectionDeletions = + $this->extraUpdates = + $this->parentAssociations = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + foreach ($this->identityMap as $className => $documents) { + if ($className === $documentName) { + foreach ($documents as $document) { + $this->doDetach($document, $visited, true); + } + } + } + } + + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->dm, $documentName)); + } + } + + /** + * INTERNAL: + * Schedules an embedded document for removal. The remove() operation will be + * invoked on that document at the beginning of the next commit of this + * UnitOfWork. + * + * @ignore + * @param object $document + */ + public function scheduleOrphanRemoval($document) + { + $this->orphanRemovals[spl_object_hash($document)] = $document; + } + + /** + * INTERNAL: + * Schedules a complete collection for removal when this UnitOfWork commits. + * + * @param PersistentCollection $coll + */ + public function scheduleCollectionDeletion(PersistentCollection $coll) + { + //TODO: if $coll is already scheduled for recreation ... what to do? + // Just remove $coll from the scheduled recreations? + $this->collectionDeletions[] = $coll; + } + + public function isCollectionScheduledForDeletion(PersistentCollection $coll) + { + return in_array($coll, $this->collectionsDeletions, true); + } + + /** + * INTERNAL: + * Creates an document. Used for reconstitution of documents during hydration. + * + * @ignore + * @param string $className The name of the document class. + * @param array $data The data for the document. + * @param array $hints Any hints to account for during reconstitution/lookup of the document. + * @return object The document instance. + * @internal Highly performance-sensitive method. + */ + public function getOrCreateDocument($className, $data, &$hints = array()) + { + $class = $this->dm->getClassMetadata($className); + + // @TODO figure out how to remove this + if ($class->discriminatorField) { + if (isset($data[$class->discriminatorField['name']])) { + $type = $data[$class->discriminatorField['name']]; + $class = $this->dm->getClassMetadata($class->discriminatorMap[$data[$class->discriminatorField['name']]]); + unset($data[$class->discriminatorField['name']]); + } + } + + $id = $class->getPHPIdentifierValue($data['_id']); + if (isset($this->identityMap[$class->rootDocumentName][$id])) { + $document = $this->identityMap[$class->rootDocumentName][$id]; + $oid = spl_object_hash($document); + if ($document instanceof Proxy && ! $document->__isInitialized__) { + $document->__isInitialized__ = true; + $overrideLocalValues = true; + if ($document instanceof NotifyPropertyChanged) { + $document->addPropertyChangedListener($this); + } + } else { + $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + } + if ($overrideLocalValues) { + $data = $this->hydratorFactory->hydrate($document, $data, $hints); + $this->originalDocumentData[$oid] = $data; + } + } else { + $document = $class->newInstance(); + $this->registerManaged($document, $id, $data); + $oid = spl_object_hash($document); + $this->documentStates[$oid] = self::STATE_MANAGED; + $this->identityMap[$class->rootDocumentName][$id] = $document; + $data = $this->hydratorFactory->hydrate($document, $data, $hints); + $this->originalDocumentData[$oid] = $data; + } + return $document; + } + + /** + * Cascades the preLoad event to embedded documents. + * + * @param ClassMetadata $class + * @param object $document + * @param array $data + */ + private function cascadePreLoad(ClassMetadata $class, $document, $data) + { + foreach ($class->fieldMappings as $mapping) { + if (isset($mapping['embedded'])) { + $value = $class->reflFields[$mapping['fieldName']]->getValue($document); + if ($value === null) { + continue; + } + if ($mapping['type'] === 'one') { + $value = array($value); + } + foreach ($value as $entry) { + $entryClass = $this->dm->getClassMetadata(get_class($entry)); + if (isset($entryClass->lifecycleCallbacks[Events::preLoad])) { + $args = array(&$data); + $entryClass->invokeLifecycleCallbacks(Events::preLoad, $entry, $args); + } + if ($this->evm->hasListeners(Events::preLoad)) { + $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($entry, $this->dm, $data[$mapping['name']])); + } + $this->cascadePreLoad($entryClass, $entry, $data[$mapping['name']]); + } + } + } + } + + /** + * Initializes (loads) an uninitialized persistent collection of a document. + * + * @param PersistentCollection $collection The collection to initialize. + */ + public function loadCollection(PersistentCollection $collection) + { + $this->getDocumentPersister(get_class($collection->getOwner()))->loadCollection($collection); + } + + /** + * Gets the identity map of the UnitOfWork. + * + * @return array + */ + public function getIdentityMap() + { + return $this->identityMap; + } + + /** + * Gets the original data of an document. The original data is the data that was + * present at the time the document was reconstituted from the database. + * + * @param object $document + * @return array + */ + public function getOriginalDocumentData($document) + { + $oid = spl_object_hash($document); + if (isset($this->originalDocumentData[$oid])) { + return $this->originalDocumentData[$oid]; + } + return array(); + } + + /** + * @ignore + */ + public function setOriginalDocumentData($document, array $data) + { + $this->originalDocumentData[spl_object_hash($document)] = $data; + } + + /** + * INTERNAL: + * Sets a property value of the original data array of an document. + * + * @ignore + * @param string $oid + * @param string $property + * @param mixed $value + */ + public function setOriginalDocumentProperty($oid, $property, $value) + { + $this->originalDocumentData[$oid][$property] = $value; + } + + /** + * Gets the identifier of an document. + * The returned value is always an array of identifier values. If the document + * has a composite identifier then the identifier values are in the same + * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). + * + * @param object $document + * @return array The identifier values. + */ + public function getDocumentIdentifier($document) + { + return isset($this->documentIdentifiers[spl_object_hash($document)]) ? + $this->documentIdentifiers[spl_object_hash($document)] : null; + } + + /** + * Checks whether the UnitOfWork has any pending insertions. + * + * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise. + */ + public function hasPendingInsertions() + { + return ! empty($this->documentInsertions); + } + + /** + * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the + * number of documents in the identity map. + * + * @return integer + */ + public function size() + { + $count = 0; + foreach ($this->identityMap as $documentSet) { + $count += count($documentSet); + } + return $count; + } + + /** + * INTERNAL: + * Registers a document as managed. + * + * @param object $document The document. + * @param array $id The identifier values. + * @param array $data The original document data. + */ + public function registerManaged($document, $id, array $data) + { + $oid = spl_object_hash($document); + if ($id === null) { + $this->documentIdentifiers[$oid] = $oid; + } else { + $this->documentIdentifiers[$oid] = $id; + } + $this->documentStates[$oid] = self::STATE_MANAGED; + $this->originalDocumentData[$oid] = $data; + $this->addToIdentityMap($document); + } + + /** + * INTERNAL: + * Clears the property changeset of the document with the given OID. + * + * @param string $oid The document's OID. + */ + public function clearDocumentChangeSet($oid) + { + $this->documentChangeSets[$oid] = array(); + } + + /* PropertyChangedListener implementation */ + + /** + * Notifies this UnitOfWork of a property change in an document. + * + * @param object $document The document that owns the property. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property. + * @param mixed $newValue The new value of the property. + */ + public function propertyChanged($document, $propertyName, $oldValue, $newValue) + { + $oid = spl_object_hash($document); + $class = $this->dm->getClassMetadata(get_class($document)); + + if ( ! isset($class->fieldMappings[$propertyName])) { + return; // ignore non-persistent fields + } + + // Update changeset and mark document for synchronization + $this->documentChangeSets[$oid][$propertyName] = array($oldValue, $newValue); + if ( ! isset($this->scheduledForDirtyCheck[$class->rootDocumentName][$oid])) { + $this->scheduleForDirtyCheck($document); + } + } + + /** + * Gets the currently scheduled document insertions in this UnitOfWork. + * + * @return array + */ + public function getScheduledDocumentInsertions() + { + return $this->documentInsertions; + } + + /** + * Gets the currently scheduled document updates in this UnitOfWork. + * + * @return array + */ + public function getScheduledDocumentUpdates() + { + return $this->documentUpdates; + } + + /** + * Gets the currently scheduled document deletions in this UnitOfWork. + * + * @return array + */ + public function getScheduledDocumentDeletions() + { + return $this->documentDeletions; + } + + /** + * Get the currently scheduled complete collection deletions + * + * @return array + */ + public function getScheduledCollectionDeletions() + { + return $this->collectionDeletions; + } + + /** + * Gets the currently scheduled collection inserts, updates and deletes. + * + * @return array + */ + public function getScheduledCollectionUpdates() + { + return $this->collectionUpdates; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * @param object + * @return void + */ + public function initializeObject($obj) + { + if ($obj instanceof Proxy) { + $obj->__load(); + } else if ($obj instanceof PersistentCollection) { + $obj->initialize(); + } + } + + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Version.php b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Version.php new file mode 100644 index 00000000..59de16bf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Version.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ODM\MongoDB; + +/** + * Class to store and retrieve the version of Doctrine + * + * @since 1.0 + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '1.0.0BETA4-DEV'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * @return int Returns -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/mongodb-odm/phpunit.xml.dist b/vendor/doctrine/mongodb-odm/phpunit.xml.dist new file mode 100644 index 00000000..945143da --- /dev/null +++ b/vendor/doctrine/mongodb-odm/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ODM/MongoDB + + + diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/BaseTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/BaseTest.php new file mode 100644 index 00000000..98355bc9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/BaseTest.php @@ -0,0 +1,82 @@ +setProxyDir(__DIR__ . '/../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $config->setDefaultDB('doctrine_odm_tests'); + + /* + $config->setLoggerCallable(function(array $log) { + print_r($log); + }); + $config->setMetadataCacheImpl(new ApcCache()); + */ + + $config->addFilter('testFilter', 'Doctrine\ODM\MongoDB\Tests\Query\Filter\Filter'); + + $reader = new AnnotationReader(); + $this->annotationDriver = new AnnotationDriver($reader, __DIR__ . '/../../../../Documents'); + $config->setMetadataDriverImpl($this->annotationDriver); + + $conn = new Connection(null, array(), $config); + $this->dm = DocumentManager::create($conn, $config); + $this->uow = $this->dm->getUnitOfWork(); + } + + protected function getTestDocumentManager($metadataDriver = null) + { + if ($metadataDriver === null) { + $metadataDriver = new MetadataDriverMock(); + } + $mongoMock = new ConnectionMock(); + $config = new \Doctrine\ODM\MongoDB\Configuration(); + $config->setProxyDir(__DIR__ . '/../../Proxies'); + $config->setProxyNamespace('Doctrine\ODM\MongoDB\Tests\Proxies'); + $eventManager = new EventManager(); + $mockDriver = new MetadataDriverMock(); + $config->setMetadataDriverImpl($metadataDriver); + + return DocumentManagerMock::create($mongoMock, $config, $eventManager); + } + + public function tearDown() + { + if ($this->dm) { + $collections = $this->dm->getConnection()->selectDatabase('doctrine_odm_tests')->listCollections(); + foreach ($collections as $collection) { + $collection->drop(); + } + } + } + + public function escape($command) + { + return $this->dm->getConfiguration()->getMongoCmd() . $command; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/CommitOrderCalculatorTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/CommitOrderCalculatorTest.php new file mode 100644 index 00000000..0173cd39 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/CommitOrderCalculatorTest.php @@ -0,0 +1,54 @@ +calc = new \Doctrine\ODM\MongoDB\Internal\CommitOrderCalculator(); + } + + public function testCommitOrdering1() + { + $class1 = new ClassMetadata(__NAMESPACE__ . '\NodeClass1'); + $class2 = new ClassMetadata(__NAMESPACE__ . '\NodeClass2'); + $class3 = new ClassMetadata(__NAMESPACE__ . '\NodeClass3'); + $class4 = new ClassMetadata(__NAMESPACE__ . '\NodeClass4'); + $class5 = new ClassMetadata(__NAMESPACE__ . '\NodeClass5'); + + $this->calc->addClass($class1); + $this->calc->addClass($class2); + $this->calc->addClass($class3); + $this->calc->addClass($class4); + $this->calc->addClass($class5); + + $this->calc->addDependency($class1, $class2); + $this->calc->addDependency($class2, $class3); + $this->calc->addDependency($class3, $class4); + $this->calc->addDependency($class5, $class1); + + $sorted = $this->calc->getCommitOrder(); + + // There is only 1 valid ordering for this constellation + $correctOrder = array($class5, $class1, $class2, $class3, $class4); + $this->assertSame($correctOrder, $sorted); + } +} + +class NodeClass1 {} +class NodeClass2 {} +class NodeClass3 {} +class NodeClass4 {} +class NodeClass5 {} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php new file mode 100644 index 00000000..208f3c94 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php @@ -0,0 +1,149 @@ + + */ +class DocumentManagerTest extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + public function testCustomRepository() + { + $dm = $this->getDocumentManager(); + $this->assertInstanceOf('Documents\CustomRepository\Repository', $dm->getRepository('Documents\CustomRepository\Document')); + } + + public function testGetConnection() + { + $this->assertInstanceOf('\Doctrine\MongoDB\Connection', $this->dm->getConnection()); + } + + public function testGetMetadataFactory() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory', $this->dm->getMetadataFactory()); + } + + public function testGetConfiguration() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\Configuration', $this->dm->getConfiguration()); + } + + public function testGetUnitOfWork() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\UnitOfWork', $this->dm->getUnitOfWork()); + } + + public function testGetProxyFactory() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\Proxy\ProxyFactory', $this->dm->getProxyFactory()); + } + + public function testGetEventManager() + { + $this->assertInstanceOf('\Doctrine\Common\EventManager', $this->dm->getEventManager()); + } + + public function testGetSchemaManager() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\SchemaManager', $this->dm->getSchemaManager()); + } + + public function testCreateQueryBuilder() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\Query\Builder', $this->dm->createQueryBuilder()); + } + + public function testGetFilterCollection() + { + $this->assertInstanceOf('\Doctrine\ODM\MongoDB\Query\FilterCollection', $this->dm->getFilterCollection()); + } + + public function testGetPartialReference() + { + $user = $this->dm->getPartialReference('Documents\CmsUser', 42); + $this->assertTrue($this->dm->contains($user)); + $this->assertEquals(42, $user->id); + $this->assertNull($user->getName()); + } + + static public function dataMethodsAffectedByNoObjectArguments() + { + return array( + array('persist'), + array('remove'), + array('merge'), + array('refresh'), + array('detach') + ); + } + + /** + * @dataProvider dataMethodsAffectedByNoObjectArguments + * @expectedException \InvalidArgumentException + * @param string $methodName + */ + public function testThrowsExceptionOnNonObjectValues($methodName) { + $this->dm->$methodName(null); + } + + static public function dataAffectedByErrorIfClosedException() + { + return array( + array('flush'), + array('persist'), + array('remove'), + array('merge'), + array('refresh'), + ); + } + + /** + * @dataProvider dataAffectedByErrorIfClosedException + * @param string $methodName + */ + public function testAffectedByErrorIfClosedException($methodName) + { + $this->setExpectedException('Doctrine\ODM\MongoDB\MongoDBException', 'closed'); + + $this->dm->close(); + if ($methodName === 'flush') { + $this->dm->$methodName(); + } else { + $this->dm->$methodName(new \stdClass()); + } + } + + protected function getDocumentManager() + { + $config = new Configuration(); + + $config->setProxyDir(__DIR__ . '/../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $config->setDefaultDB('doctrine_odm_tests'); + + /* + $config->setLoggerCallable(function(array $log) { + print_r($log); + }); + $config->setMetadataCacheImpl(new ApcCache()); + */ + + $reader = new AnnotationReader(); + $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents')); + return DocumentManager::create($this->getConnection(), $config); + } + + protected function getConnection() + { + return $this->getMock('Doctrine\MongoDB\Connection'); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleCallbacksTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleCallbacksTest.php new file mode 100644 index 00000000..97d2cca4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleCallbacksTest.php @@ -0,0 +1,299 @@ +name = 'jon'; + $user->profile = new Profile(); + $user->profile->name = 'Jonathan H. Wage'; + $this->dm->persist($user); + $this->dm->flush(); + return $user; + } + + public function testPreUpdateChangingValue() + { + $user = $this->createUser(); + $this->dm->clear(); + + $user = $this->dm->find(__NAMESPACE__.'\User', $user->id); + $this->assertInstanceOf('DateTime', $user->createdAt); + $this->assertInstanceOf('DateTime', $user->profile->createdAt); + + $user->name = 'jon changed'; + $user->profile->name = 'changed'; + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find(__NAMESPACE__.'\User', $user->id); + $this->assertInstanceOf('DateTime', $user->updatedAt); + $this->assertInstanceOf('DateTime', $user->profile->updatedAt); + } + + public function testPreAndPostPersist() + { + $user = $this->createUser(); + $this->assertTrue($user->prePersist); + $this->assertTrue($user->profile->prePersist); + + $this->assertTrue($user->postPersist); + $this->assertTrue($user->profile->postPersist); + } + + public function testPreUpdate() + { + $user = $this->createUser(); + $user->name = 'jwage'; + $user->profile->name = 'Jon Doe'; + $this->dm->flush(); + + $this->assertTrue($user->preUpdate); + $this->assertTrue($user->profile->preUpdate); + + $this->assertTrue($user->postUpdate); + $this->assertTrue($user->profile->postUpdate); + } + + public function testPreFlush() + { + $user = $this->createUser(); + $user->name = 'jwage'; + $user->profile->name = 'Jon Doe'; + $this->dm->flush(); + + $this->assertTrue($user->preFlush); + $this->assertTrue($user->profile->preFlush); + } + + public function testPreLoadAndPostLoad() + { + $user = $this->createUser(); + $this->dm->clear(); + + $user = $this->dm->find(__NAMESPACE__.'\User', $user->id); + + $this->assertTrue($user->preLoad); + $this->assertTrue($user->profile->preLoad); + $this->assertTrue($user->postLoad); + $this->assertTrue($user->profile->postLoad); + } + + public function testPreAndPostRemove() + { + $user = $this->createUser(); + $this->dm->remove($user); + $this->dm->flush(); + + $this->assertTrue($user->preRemove); + $this->assertTrue($user->profile->preRemove); + + $this->assertTrue($user->postRemove); + $this->assertTrue($user->profile->postRemove); + } + + public function testEmbedManyEvent() + { + $user = new User(); + $user->name = 'jon'; + $profile = new Profile(); + $profile->name = 'testing cool ya'; + $user->profiles[] = $profile; + + $this->dm->persist($user); + $this->dm->flush(); + + $this->assertTrue($profile->prePersist); + $this->assertTrue($profile->postPersist); + $this->assertFalse($profile->preUpdate); + $this->assertFalse($profile->postUpdate); + + $profile->name = 'changed'; + $this->dm->flush(); + + $this->assertTrue($profile->preUpdate); + $this->assertTrue($profile->postUpdate); + + $this->dm->clear(); + $user = $this->dm->find(__NAMESPACE__.'\User', $user->id); + $profile = $user->profiles[0]; + + $this->assertTrue($profile->preLoad); + $this->assertTrue($profile->postLoad); + + $profile->name = 'w00t'; + $this->dm->flush(); + + $this->assertTrue($user->preUpdate); + $this->assertTrue($user->postUpdate); + $this->assertTrue($profile->preUpdate); + $this->assertTrue($profile->postUpdate); + + $this->dm->remove($user); + $this->dm->flush(); + + $this->assertTrue($user->preRemove); + $this->assertTrue($user->postRemove); + $this->assertTrue($profile->preRemove); + $this->assertTrue($profile->postRemove); + } + + public function testMultipleLevelsOfEmbedded() + { + $user = $this->createUser(); + $profile = new Profile(); + $profile->name = '2nd level'; + $user->profile->profile = $profile; + $this->dm->flush(); + + $this->assertTrue($profile->prePersist); + $this->assertTrue($profile->postPersist); + $this->assertFalse($profile->preUpdate); + $this->assertFalse($profile->postUpdate); + + $profile->name = '2nd level changed'; + $this->dm->flush(); + + $this->assertTrue($profile->preUpdate); + $this->assertTrue($profile->postUpdate); + + $this->dm->clear(); + $user = $this->dm->find(__NAMESPACE__.'\User', $user->id); + $profile = $user->profile->profile; + $profile->name = '2nd level changed again'; + + $profile2 = new Profile(); + $profile2->name = 'test'; + $user->profiles[] = $profile2; + $this->dm->flush(); + + $this->assertFalse($profile->prePersist); + $this->assertFalse($profile->postPersist); + $this->assertTrue($profile->preUpdate); + $this->assertTrue($profile->postUpdate); + + $this->assertTrue($profile2->prePersist); + $this->assertTrue($profile2->postPersist); + $this->assertFalse($profile2->preUpdate); + $this->assertFalse($profile2->postUpdate); + + $this->dm->remove($user); + $this->dm->flush(); + + $this->assertTrue($user->preRemove); + $this->assertTrue($user->postRemove); + + $this->assertTrue($user->profile->preRemove); + $this->assertTrue($user->profile->postRemove); + + $this->assertTrue($user->profile->profile->preRemove); + $this->assertTrue($user->profile->profile->postRemove); + + $this->assertTrue($user->profiles[0]->preRemove); + $this->assertTrue($user->profiles[0]->postRemove); + } +} + +/** @ODM\Document */ +class User extends BaseDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\EmbedOne(targetDocument="Profile") */ + public $profile; + + /** @ODM\EmbedMany(targetDocument="Profile") */ + public $profiles = array(); +} + +/** @ODM\EmbeddedDocument */ +class Profile extends BaseDocument +{ + /** @ODM\EmbedOne(targetDocument="Profile") */ + public $profile; +} + +/** @ODM\MappedSuperclass */ +abstract class BaseDocument +{ + /** @ODM\String */ + public $name; + + /** @ODM\Date */ + public $createdAt; + + /** @ODM\Date */ + public $updatedAt; + + public $prePersist = false; + public $postPersist = false; + public $preUpdate = false; + public $postUpdate = false; + public $preRemove = false; + public $postRemove = false; + public $preLoad = false; + public $postLoad = false; + public $preFlush = false; + + /** @ODM\PrePersist */ + public function prePersist() + { + $this->prePersist = true; + $this->createdAt = new \DateTime(); + } + + /** @ODM\PostPersist */ + public function postPersist() + { + $this->postPersist = true; + } + + /** @ODM\PreUpdate */ + public function preUpdate() + { + $this->preUpdate = true; + $this->updatedAt = new \DateTime(); + } + + /** @ODM\PostUpdate */ + public function postUpdate() + { + $this->postUpdate = true; + } + + /** @ODM\PreRemove */ + public function preRemove() + { + $this->preRemove = true; + } + + /** @ODM\PostRemove */ + public function postRemove() + { + $this->postRemove = true; + } + + /** @ODM\PreLoad */ + public function preLoad() + { + $this->preLoad = true; + } + + /** @ODM\PostLoad */ + public function postLoad() + { + $this->postLoad = true; + } + + /** @ODM\PreFlush */ + public function preFlush() + { + $this->preFlush = true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php new file mode 100644 index 00000000..51a84f6f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php @@ -0,0 +1,223 @@ +listener = new MyEventListener(); + $evm = $this->dm->getEventManager(); + $events = array( + Events::prePersist, + Events::postPersist, + Events::preUpdate, + Events::postUpdate, + Events::preLoad, + Events::postLoad, + Events::preRemove, + Events::postRemove + ); + $evm->addEventListener($events, $this->listener); + return $this->dm; + } + + public function testLifecycleListeners() + { + $dm = $this->getDocumentManager(); + + $test = new TestDocument(); + $test->name = 'test'; + $dm->persist($test); + $dm->flush(); + + $called = array( + Events::prePersist => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument'), + Events::postPersist => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $test->embedded[0] = new TestEmbeddedDocument(); + $test->embedded[0]->name = 'cool'; + $dm->flush(); + $dm->clear(); + + $called = array( + Events::prePersist => array('Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument'), + Events::preUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument'), + Events::postUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument'), + Events::postPersist => array('Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $document = $dm->find(__NAMESPACE__.'\TestDocument', $test->id); + $document->embedded->initialize(); + $called = array( + Events::preLoad => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument'), + Events::postLoad => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $document->embedded[0]->name = 'changed'; + $dm->flush(); + + $called = array( + Events::preUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument'), + Events::postUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $dm->remove($document); + $dm->flush(); + + $called = array( + Events::preRemove => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument'), + Events::postRemove => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument', 'Doctrine\ODM\MongoDB\Tests\Events\TestEmbeddedDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $test = new TestDocument(); + $test->name = 'test'; + $test->embedded[0] = new TestEmbeddedDocument(); + $test->embedded[0]->name = 'cool'; + $dm->persist($test); + $dm->flush(); + $this->listener->called = array(); + + $test->name = 'cool'; + $dm->flush(); + + $dm->clear(); + + $called = array( + Events::preUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument'), + Events::postUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestDocument') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + } + + public function testMultipleLevelsOfEmbeddedDocsPrePersist() + { + $dm = $this->getDocumentManager(); + + $test = new TestProfile(); + $test->name = 'test'; + $test->image = new Image('Test Image'); + $dm->persist($test); + $dm->flush(); + $dm->clear(); + + $test = $dm->find(__NAMESPACE__.'\TestProfile', $test->id); + $this->listener->called = array(); + + $test->image->thumbnails[] = new Thumbnail('Thumbnail #1'); + + $dm->flush(); + $called = array( + Events::prePersist => array('Doctrine\ODM\MongoDB\Tests\Events\Thumbnail'), + Events::preUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestProfile', 'Doctrine\ODM\MongoDB\Tests\Events\Image'), + Events::postUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestProfile', 'Doctrine\ODM\MongoDB\Tests\Events\Image'), + Events::postPersist => array('Doctrine\ODM\MongoDB\Tests\Events\Thumbnail') + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + + $test->image->thumbnails[0]->name = 'ok'; + $dm->flush(); + $called = array( + Events::preUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestProfile', 'Doctrine\ODM\MongoDB\Tests\Events\Image', 'Doctrine\ODM\MongoDB\Tests\Events\Thumbnail'), + Events::postUpdate => array('Doctrine\ODM\MongoDB\Tests\Events\TestProfile', 'Doctrine\ODM\MongoDB\Tests\Events\Image', 'Doctrine\ODM\MongoDB\Tests\Events\Thumbnail'), + ); + $this->assertEquals($called, $this->listener->called); + $this->listener->called = array(); + } +} + +class MyEventListener +{ + public $called = array(); + + public function __call($method, $args) + { + $document = $args[0]->getDocument(); + $className = get_class($document); + $this->called[$method][] = $className; + } +} + +/** @ODM\Document */ +class TestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="TestEmbeddedDocument") */ + public $embedded; + + /** @ODM\EmbedOne(targetDocument="Image") */ + public $image; +} + +/** @ODM\EmbeddedDocument */ +class TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; +} + + +/** @ODM\Document */ +class TestProfile +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedOne(targetDocument="Image") */ + public $image; +} + +/** + * @ODM\EmbeddedDocument + */ +class Image +{ + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="Thumbnail") */ + public $thumbnails = array(); + + public function __construct($name) + { + $this->name = $name; + } +} + +/** + * @ODM\EmbeddedDocument + */ +class Thumbnail +{ + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php new file mode 100644 index 00000000..3005a2c1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php @@ -0,0 +1,67 @@ +$field = $data; + $this->dm->persist($test); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(get_class($test))->findOne(array()); + $this->assertInstanceOf('MongoBinData', $check[$field]); + $this->assertEquals($type, $check[$field]->type); + $this->assertEquals($data, $check[$field]->bin); + } + + public function provideData() + { + /* In driver versions before 1.2.11, the custom binary data type is + * incorrectly returned as -128. + * + * See: https://jira.mongodb.org/browse/PHP-408 + */ + $expectedBinCustom = (-1 === version_compare('1.2.10', \Mongo::VERSION)) + ? \MongoBinData::CUSTOM + : -128; + + return array( + array('bin', 'test', \MongoBinData::BYTE_ARRAY), + array('binFunc', 'test', \MongoBinData::FUNC), + array('BinUUID', 'test', \MongoBinData::UUID), + array('binMd5', 'test', \MongoBinData::MD5), + array('binCustom', 'test', $expectedBinCustom), + ); + } +} + +/** @ODM\Document */ +class BinDataTestUser +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Bin */ + public $bin; + + /** @ODM\Bin(type="bin_func") */ + public $binFunc; + + /** @ODM\Bin(type="bin_uuid") */ + public $BinUUID; + + /** @ODM\Bin(type="bin_md5") */ + public $binMd5; + + /** @ODM\Bin(type="bin_custom") */ + public $binCustom; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php new file mode 100644 index 00000000..01854869 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php @@ -0,0 +1,328 @@ +getCollectionPersister(); + $user = $this->getTestUser('jwage'); + $persister->delete($user->phonenumbers, array('safe' => true)); + + $user = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertFalse(isset($user['phonenumbers']), 'Test that the phonenumbers field was deleted'); + } + + public function testDeleteEmbedMany() + { + $persister = $this->getCollectionPersister(); + $user = $this->getTestUser('jwage'); + $persister->delete($user->categories, array('safe' => true)); + + $user = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertFalse(isset($user['categories']), 'Test that the categories field was deleted'); + } + + public function testDeleteNestedEmbedMany() + { + $persister = $this->getCollectionPersister(); + $user = $this->getTestUser('jwage'); + + $persister->delete($user->categories[0]->children[0]->children, array('safe' => true)); + $persister->delete($user->categories[0]->children[1]->children, array('safe' => true)); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + + $this->assertFalse(isset($check['categories']['0']['children'][0]['children'])); + $this->assertFalse(isset($check['categories']['0']['children'][1]['children'])); + + $persister->delete($user->categories[0]->children, array('safe' => true)); + $persister->delete($user->categories[1]->children, array('safe' => true)); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + + $this->assertFalse(isset($check['categories'][0]['children']), 'Test that the nested children categories field was deleted'); + $this->assertTrue(isset($check['categories'][0]), 'Test that the category with the children still exists'); + + $this->assertFalse(isset($check['categories'][1]['children']), 'Test that the nested children categories field was deleted'); + $this->assertTrue(isset($check['categories'][1]), 'Test that the category with the children still exists'); + } + + public function testDeleteRows() + { + $persister = $this->getCollectionPersister(); + $user = $this->getTestUser('jwage'); + + unset($user->phonenumbers[0]); + unset($user->phonenumbers[1]); + + unset($user->categories[0]->children[0]->children[0]); + unset($user->categories[0]->children[0]->children[1]); + + unset($user->categories[0]->children[1]->children[0]); + unset($user->categories[0]->children[1]->children[1]); + + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + + $this->assertFalse(isset($check['phonenumbers'][0])); + $this->assertFalse(isset($check['phonenumbers'][1])); + + $this->assertFalse(isset($check['categories'][0]['children'][0]['children'][0])); + $this->assertFalse(isset($check['categories'][0]['children'][0]['children'][1])); + + $this->assertFalse(isset($check['categories'][0]['children'][1]['children'][0])); + $this->assertFalse(isset($check['categories'][0]['children'][1]['children'][1])); + + unset($user->categories[0]); + unset($user->categories[1]); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertFalse(isset($check['categories'][0])); + $this->assertFalse(isset($check['categories'][1])); + } + + public function testInsertRows() + { + $user = $this->getTestUser('jwage'); + $user->phonenumbers[] = new CollectionPersisterPhonenumber('6155139185'); + $user->phonenumbers[] = new CollectionPersisterPhonenumber('6155139185'); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertEquals(4, count($check['phonenumbers'])); + $this->assertEquals((string) $check['phonenumbers'][2]['$id'], $user->phonenumbers[2]->id); + $this->assertEquals((string) $check['phonenumbers'][3]['$id'], $user->phonenumbers[3]->id); + + $user->categories[] = new CollectionPersisterCategory('Test'); + $user->categories[] = new CollectionPersisterCategory('Test'); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertEquals(4, count($check['categories'])); + + $user->categories[3]->children[0] = new CollectionPersisterCategory('Test'); + $user->categories[3]->children[1] = new CollectionPersisterCategory('Test'); + $user->categories[3]->children[1]->children[0] = new CollectionPersisterCategory('Test'); + $user->categories[3]->children[1]->children[1] = new CollectionPersisterCategory('Test'); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterUser')->findOne(array('username' => 'jwage')); + $this->assertEquals(2, count($check['categories'][3]['children'])); + $this->assertEquals(2, count($check['categories'][3]['children']['1']['children'])); + } + + private function getTestUser($username) + { + $user = new CollectionPersisterUser(); + $user->username = $username; + $user->phonenumbers[0] = new CollectionPersisterPhonenumber('6155139185'); + $user->phonenumbers[1] = new CollectionPersisterPhonenumber('6155139185'); + + $user->categories[0] = new CollectionPersisterCategory('Category0'); + $user->categories[1] = new CollectionPersisterCategory('Category1'); + + $user->categories[0]->children[0] = new CollectionPersisterCategory('Child of Category0 1'); + $user->categories[0]->children[1] = new CollectionPersisterCategory('Child of Category0 2'); + + $user->categories[1]->children[0] = new CollectionPersisterCategory('Child of Category1 1'); + $user->categories[1]->children[1] = new CollectionPersisterCategory('Child of Category1 2'); + + $user->categories[0]->children[0]->children[0] = new CollectionPersisterCategory('Child of Category1_0 1'); + $user->categories[0]->children[0]->children[1] = new CollectionPersisterCategory('Child of Category1_0 2'); + + $user->categories[0]->children[1]->children[0] = new CollectionPersisterCategory('Child of Category1_1 1'); + $user->categories[0]->children[1]->children[1] = new CollectionPersisterCategory('Child of Category1_1 2'); + + $this->dm->persist($user); + $this->dm->flush(null, array('safe' => true)); + return $user; + } + + private function getCollectionPersister() + { + $uow = $this->dm->getUnitOfWork(); + $pb = new PersistenceBuilder($this->dm, $uow, '$'); + return new CollectionPersister($this->dm, $pb, $uow, '$'); + } + + public function testNestedEmbedManySetStrategy() + { + $post = new CollectionPersisterPost("Doest it work?"); + $comment = new CollectionPersisterComment("no way...", "skeptic"); + $comment2 = new CollectionPersisterComment("Hell yeah!", "asafdav"); + $comment3 = new CollectionPersisterComment("Awesome", "all"); + + $post->comments->set('first', $comment); + $comment->comments->set('first', $comment2); + $comment->comments->set('second', $comment3); + + $this->dm->persist($post); + $this->dm->flush(null, array('safe' => true)); + + /** @var CollectionPersisterPost $check */ + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterPost')->findOne(array('post' => 'Doest it work?')); + $this->assertEquals(1, count($check['comments']), 'First level persisted correctly'); + $this->assertTrue(isset($check['comments']['first'])); + $this->assertEquals(2, count($check['comments']['first']['comments']), 'Second level persisted correctly'); + $this->assertTrue(isset($check['comments']['first']['comments']['first'])); + $this->assertTrue(isset($check['comments']['first']['comments']['second'])); + + // Test add comments + $comment4 = new CollectionPersisterComment("Does add comment work?", "Someone"); + $comment5 = new CollectionPersisterComment("Sure!", "asafdav"); + + $post->comments->set('second', $comment4); + $comment4->comments->set('just-a-key', $comment5); + + $this->dm->persist($post); + $this->dm->flush(null, array('safe' => true)); + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterPost')->findOne(array('post' => 'Doest it work?')); + + $this->assertEquals(2, count($check['comments']), 'First level persisted correctly'); + $this->assertTrue(isset($check['comments']['first'])); + $this->assertEquals(2, count($check['comments']['first']['comments']), 'Second level persisted correctly'); + $this->assertTrue(isset($check['comments']['first']['comments']['first'])); + $this->assertTrue(isset($check['comments']['first']['comments']['second'])); + $this->assertTrue(isset($check['comments']['second'])); + $this->assertEquals($comment4->comment, $check['comments']['second']['comment']); + $this->assertEquals(1, count($check['comments']['second']['comments']), 'New comment persisted correctly'); + $this->assertTrue(isset($check['comments']['second']['comments']['just-a-key'])); + $this->assertEquals($comment5->comment, $check['comments']['second']['comments']['just-a-key']['comment']); + + // Update two comments + $comment4->comment = "Sorry, I could tell"; + $comment3->comment = "Hallelujah"; + + $this->dm->persist($post); + $this->dm->flush(null, array('safe' => true)); + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterPost')->findOne(array('post' => 'Doest it work?')); + + $this->assertEquals(2, count($check['comments']), 'First level persisted correctly'); + $this->assertTrue(isset($check['comments']['first'])); + $this->assertEquals(2, count($check['comments']['first']['comments']), 'Second level persisted correctly'); + $this->assertTrue(isset($check['comments']['first']['comments']['first'])); + $this->assertTrue(isset($check['comments']['first']['comments']['second'])); + $this->assertEquals($comment3->comment, $check['comments']['first']['comments']['second']['comment']); + $this->assertTrue(isset($check['comments']['second'])); + $this->assertEquals($comment4->comment, $check['comments']['second']['comment']); + $this->assertEquals(1, count($check['comments']['second']['comments']), 'New comment persisted correctly'); + $this->assertTrue(isset($check['comments']['second']['comments']['just-a-key'])); + $this->assertEquals($comment5->comment, $check['comments']['second']['comments']['just-a-key']['comment']); + + // Delete comment + unset($post->comments['second']); + $this->dm->persist($post); + $this->dm->flush(null, array('safe' => true)); + $check = $this->dm->getDocumentCollection(__NAMESPACE__ . '\CollectionPersisterPost')->findOne(array('post' => 'Doest it work?')); + + $this->assertEquals(1, count($check['comments']), 'First level persisted correctly'); + $this->assertTrue(isset($check['comments']['first'])); + $this->assertEquals(2, count($check['comments']['first']['comments']), 'Second level persisted correctly'); + $this->assertTrue(isset($check['comments']['first']['comments']['first'])); + $this->assertTrue(isset($check['comments']['first']['comments']['second'])); + $this->assertEquals($comment3->comment, $check['comments']['first']['comments']['second']['comment']); + $this->assertFalse(isset($check['comments']['second'])); + } +} + +/** @ODM\Document(collection="user_collection_persister_test") */ +class CollectionPersisterUser +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\EmbedMany(targetDocument="CollectionPersisterCategory") */ + public $categories = array(); + + /** @ODM\ReferenceMany(targetDocument="CollectionPersisterPhonenumber", cascade={"persist"}) */ + public $phonenumbers = array(); +} + +/** @ODM\EmbeddedDocument */ +class CollectionPersisterCategory +{ + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="CollectionPersisterCategory") */ + public $children = array(); + + public function __construct($name) + { + $this->name = $name; + } +} + +/** @ODM\Document(collection="phonenumber_collection_persister_test") */ +class CollectionPersisterPhonenumber +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $phonenumber; + + public function __construct($phonenumber) + { + $this->phonenumber = $phonenumber; + } +} + +/** @ODM\Document(collection="post_collection_persister_test") */ +class CollectionPersisterPost +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $post; + + /** @ODM\EmbedMany(targetDocument="CollectionPersisterComment", strategy="set") */ + public $comments = array(); + + function __construct($post) + { + $this->comments = new \Doctrine\Common\Collections\ArrayCollection(); + $this->post = $post; + } + + +} + +/** @ODM\EmbeddedDocument */ +class CollectionPersisterComment +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $comment; + + /** @ODM\String */ + public $by; + + /** @ODM\EmbedMany(targetDocument="CollectionPersisterComment", strategy="set") */ + public $comments = array(); + + function __construct($comment, $by) + { + $this->comments = new \Doctrine\Common\Collections\ArrayCollection(); + $this->comment = $comment; + $this->by = $by; + } +} + + + diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionsTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionsTest.php new file mode 100644 index 00000000..640e1320 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionsTest.php @@ -0,0 +1,110 @@ +addLocation(new Location('West Nashville')); + $bar->addLocation(new Location('East Nashville')); + $bar->addLocation(new Location('North Nashville')); + $this->dm->persist($bar); + $this->dm->flush(); + $this->dm->clear(); + + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + + $this->assertNotNull($bar); + $locations = $bar->getLocations(); + unset($locations[0]); + $locations[1]->setName('changed'); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection('Documents\Bars\Bar')->findOne(); + $this->assertEquals(2, count($test['locations'])); + + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + $this->assertNotNull($bar); + $locations = $bar->getLocations(); + $this->assertEquals(2, count($locations)); + $this->assertEquals('changed', $locations[0]->getName()); + + unset($locations[0], $locations[1]); + $this->dm->flush(); + $this->dm->clear(); + + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + $this->assertNotNull($bar); + $locations = $bar->getLocations(); + $this->assertEquals(0, count($locations)); + + $bar->addLocation(new Location('West Nashville')); + $bar->addLocation(new Location('East Nashville')); + $bar->addLocation(new Location('North Nashville')); + $this->dm->flush(); + $this->dm->clear(); + + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + $this->assertEquals($bar->getId(), $this->dm->getUnitOfWork()->getDocumentIdentifier($bar)); + + $this->assertNotNull($bar); + $locations = $bar->getLocations(); + $this->assertEquals(3, count($locations)); + $locations = $bar->getLocations(); + $locations->clear(); + $this->assertEquals(0, count($locations)); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + $locations = $bar->getLocations(); + $this->assertEquals(0, count($locations)); + } + + public function testCreateCollections() + { + $sm = $this->dm->getSchemaManager(); + $sm->dropDocumentCollection(__NAMESPACE__.'\CreateCollectionTest'); + $sm->createDocumentCollection(__NAMESPACE__.'\CreateCollectionTest'); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\CreateCollectionTest'); + $insert = array(array(1), array(2), array(3)); + $coll->batchInsert($insert, array('safe' => true, 'fsync' => true)); + + $data = iterator_to_array($coll->find()); + $this->assertEquals(3, count($data)); + } +} + +/** + * @ODM\Document(collection={ + * "name"="testing", + * "capped"="true", + * "size"="1000", + * "max"="1" + * }) + */ +class CollectionTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; +} + +/** + * @ODM\Document + */ +class CreateCollectionTest +{ + /** @ODM\Id */ + public $id; + +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CursorHydrationTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CursorHydrationTest.php new file mode 100644 index 00000000..b61428fe --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CursorHydrationTest.php @@ -0,0 +1,36 @@ +user = new User(); + $this->user->setUsername('foo'); + + $this->dm->persist($this->user); + $this->dm->flush(); + + $this->repository = $this->dm->getRepository('Documents\User'); + } + + public function testCursorShouldHydrateCurrent() + { + $cursor = $this->repository->findAll(); + $cursor->next(); + + $this->assertSame($this->user, $cursor->current()); + } + + public function testCursorShouldHydrateGetNext() + { + $cursor = $this->repository->findAll(); + + $this->assertSame($this->user, $cursor->getNext()); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomFieldNameTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomFieldNameTest.php new file mode 100644 index 00000000..814c01f2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomFieldNameTest.php @@ -0,0 +1,108 @@ +username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\CustomFieldName')->findOne(); + $this->assertTrue(isset($test['login'])); + $this->assertEquals('test', $test['login']); + } + + public function testHydration() + { + $test = new CustomFieldName(); + $test->username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find(__NAMESPACE__.'\CustomFieldName', $test->id); + $this->assertNotNull($test); + $this->assertEquals('test', $test->username); + } + + public function testUpdateSetsLoginInsteadOfUsername() + { + $test = new CustomFieldName(); + $test->username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find(__NAMESPACE__.'\CustomFieldName', $test->id); + + $test->username = 'ok'; + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\CustomFieldName')->findOne(); + $this->assertTrue(isset($test['login'])); + $this->assertEquals('ok', $test['login']); + } + + public function testFindOneQueryIsPrepared() + { + $test = new CustomFieldName(); + $test->username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository(__NAMESPACE__.'\CustomFieldName')->findOneBy(array('username' => 'test')); + $this->assertNotNull($test); + $this->assertEquals('test', $test->username); + } + + public function testFindQueryIsPrepared() + { + $test = new CustomFieldName(); + $test->username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository(__NAMESPACE__.'\CustomFieldName')->findOneBy(array('username' => 'test')); + $this->assertNotNull($test); + $this->assertEquals('test', $test->username); + } + + public function testQueryBuilderAndDqlArePrepared() + { + $test = new CustomFieldName(); + $test->username = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\CustomFieldName')->field('username')->equals('test'); + $query = $qb->getQuery(); + $test = $query->getSingleResult(); + $this->assertNotNull($test); + $this->assertEquals('test', $test->username); + } +} + +/** @ODM\Document */ +class CustomFieldName +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String(name="login") */ + public $username; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomIdTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomIdTest.php new file mode 100644 index 00000000..9c1028b0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomIdTest.php @@ -0,0 +1,149 @@ + + */ +class CustomIdTest extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + public function testSetId() + { + $account = new Account(); + $account->setName('Jon Test Account'); + + $user = new CustomUser(); + $user->setId('userId'); + $user->setUsername('jon'); + $user->setPassword('changeme'); + $user->setAccount($account); + + $this->assertEquals('userId', $user->getId()); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->assertEquals('userId', $user->getId()); + + $this->dm->clear(); + + $user = $this->dm->find('Documents\CustomUser', $user->getId()); + + $this->assertEquals('userId', $user->getId()); + + $this->dm->clear(); + unset($user); + + $user = $this->dm->find('Documents\CustomUser', 'userId'); + + $this->assertNotNull($user); + + $this->assertEquals('userId', $user->getId()); + } + + public function testBatchInsertCustomId() + { + $account = new Account(); + $account->setName('Jon Test Account'); + + $user1 = new CustomUser(); + $user1->setId('userId'); + $user1->setUsername('user1'); + $user1->setPassword('changeme'); + $user1->setAccount($account); + + $user2 = new User(); + $user2->setUsername('user2'); + $user2->setPassword('changeme'); + $user2->setAccount($account); + + $user3 = new User(); + $user3->setUsername('user3'); + $user3->setPassword('changeme'); + $user3->setAccount($account); + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->persist($user3); + + $this->dm->flush(); + $this->dm->clear(); + + unset($user1, $user2, $user3); + + $users = $this->dm->getRepository("Documents\User")->findAll(); + + $this->assertEquals(2, $users->count()); + + $results = array(); + foreach ($users as $user) { + if ($user->getId() === 'userId') { + $results['userId'] = true; + } + $this->assertNotNull($user->getId()); + $results['ids'][] = $user->getId(); + } + + $users = $this->dm->getRepository("Documents\CustomUser")->findAll(); + + $this->assertEquals(1, $users->count()); + + foreach ($users as $user) { + if ($user->getId() === 'userId') { + $results['userId'] = true; + } + $this->assertNotNull($user->getId()); + $results['ids'][] = $user->getId(); + } + + $this->assertTrue($results['userId']); + $this->assertEquals(3, count($results['ids'])); + } + + public function testFindUser() + { + $account = new Account(); + $account->setName('Jon Test Account'); + + $user1 = new CustomUser(); + $user1->setId('userId'); + $user1->setUsername('user1'); + $user1->setPassword('changeme'); + $user1->setAccount($account); + + $user2 = new User(); + $user2->setUsername('user2'); + $user2->setPassword('changeme'); + $user2->setAccount($account); + + $user3 = new User(); + $user3->setUsername('user3'); + $user3->setPassword('changeme'); + $user3->setAccount($account); + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->persist($user3); + + $this->dm->flush(); + $this->dm->clear(); + + unset($user1, $user2, $user3); + + $user = $this->dm->find('Documents\CustomUser', 'userId'); + + $this->assertNotNull($user); + $this->assertEquals('userId', $user->getId()); + $this->assertEquals('user1', $user->getUsername()); + + $this->dm->clear(); + unset($user); + + $this->assertNull($this->dm->find('Documents\User', 'userId')); + $this->assertNull($this->dm->find('Documents\CustomUser', 'asd')); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomTypeTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomTypeTest.php new file mode 100644 index 00000000..a40338f9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomTypeTest.php @@ -0,0 +1,112 @@ +nationalHolidays = array(new \DateTime(), new \DateTime()); + + $this->dm->persist($country); + $this->dm->flush(); + + $this->dm->clear(); + + $country = $this->dm->find(__NAMESPACE__ . '\Country', $country->id); + + $this->assertContainsOnly('DateTime', $country->nationalHolidays); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\Tests\Functional\CustomTypeException + */ + public function testConvertToDatabaseValueExpectsArray() + { + $country = new Country(); + $country->nationalHolidays = new \DateTime(); + + $this->dm->persist($country); + $this->dm->flush(); + } +} + +class DateCollectionType extends Type +{ + // Note: this method is called by PersistenceBuilder + public function convertToDatabaseValue($value) + { + if ($value === null) { + return null; + } + + if (!is_array($value)) { + throw new CustomTypeException('Array expected.'); + } + + $converter = new DateType(); + + $value = array_map(function($date) use ($converter) { + return $converter->convertToDatabaseValue($date); + }, array_values($value)); + + return $value; + } + + // Note: this method is never called for non-identifier fields + public function convertToPHPValue($value) + { + if ($value === null) { + return null; + } + + if (!is_array($value)) { + throw new CustomTypeException('Array expected.'); + } + + $converter = new DateType(); + + $value = array_map(function($date) use ($converter) { + return $converter->convertToPHPValue($date); + }, array_values($value)); + + return $value; + } + + // Note: this method is never called + public function closureToMongo() + { + return '$return = array_map(function($v) { if ($v instanceof \DateTime) { $v = $v->getTimestamp(); } else if (is_string($v)) { $v = strtotime($v); } return new \MongoDate($v); }, $value);'; + } + + // Note: this code ends up in the generated hydrator class (via HydratorFactory) + public function closureToPHP() + { + return '$return = array_map(function($v) { if ($v instanceof \MongoDate) { $date = new \DateTime(); $date->setTimestamp($v->sec); return $date; } else { return new \DateTime($v); } }, $value);'; + } +} + +class CustomTypeException extends \Exception +{ +} + +/** @ODM\Document */ +class Country +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Field(type="date_collection") */ + public $nationalHolidays; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php new file mode 100644 index 00000000..0b6801f7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php @@ -0,0 +1,70 @@ +setProxyDir(__DIR__ . '/../../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $reader = new AnnotationReader(); + $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents')); + $config->setDefaultDB('testing'); + + $this->dm = DocumentManager::create(new Connection(), $config); + } + + public function testDefaultDatabase() + { + $this->assertEquals('testing', $this->dm->getDocumentDatabase('Doctrine\ODM\MongoDB\Tests\Functional\DefaultDatabaseTest')->getName()); + } +} + +/** @ODM\Document(collection="test") */ +class DefaultDatabaseTest +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; + + public function getId() + { + return $this->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/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php new file mode 100644 index 00000000..be1a8cc8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php @@ -0,0 +1,98 @@ +setUsername('w00ting'); + $this->dm->persist($user); + $this->dm->flush(); + + $this->assertTrue($user->getCreatedAt() instanceof \DateTime); + + $user->setCreatedAt('1985-09-01 00:00:00'); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User')->findOneByUsername('w00ting'); + $this->assertNotNull($user); + $this->assertEquals('w00ting', $user->getUsername()); + $this->assertTrue($user->getCreatedAt() instanceof \DateTime); + $this->assertEquals('09/01/1985', $user->getCreatedAt()->format('m/d/Y')); + } + + /** + * @dataProvider provideEquivalentDates + */ + public function testDateInstanceChangeDoesNotCauseUpdateIfValueIsTheSame($oldValue, $newValue) + { + $user = new User(); + $user->setCreatedAt($oldValue); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository(get_class($user))->findOneBy(array()); + $user->setCreatedAt($newValue); + $this->dm->getUnitOfWork()->computeChangeSets(); + $changeset = $this->dm->getUnitOfWork()->getDocumentChangeset($user); + $this->assertEmpty($changeset); + } + + public function provideEquivalentDates() + { + return array( + array(new \DateTime('1985-09-01 00:00:00'), new \DateTime('1985-09-01 00:00:00')), + array(new \DateTime('2012-07-11T14:55:14-04:00'), new \DateTime('2012-07-11T19:55:14+01:00')), + array(new \DateTime('@1342033881'), new \MongoDate(1342033881)), + ); + } + + public function testDateInstanceValueChangeDoesCauseUpdateIfValueIsTheSame() + { + $user = new User(); + $user->setCreatedAt(new \DateTime('1985-09-01')); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository(get_class($user))->findOneBy(array()); + $user->getCreatedAt()->setTimestamp(time() - 3600); + + $this->dm->getUnitOfWork()->computeChangeSets(); + $changeset = $this->dm->getUnitOfWork()->getDocumentChangeset($user); + $this->assertNotEmpty($changeset); + } + + public function testOldDate() + { + $user = new User(); + $user->setUsername('datetest'); + $user->setCreatedAt('1900-01-01'); + $this->dm->persist($user); + $this->dm->flush(); + + $user->setUsername('datetest2'); + $this->dm->flush(); + + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection('Documents\User')->findOne(array('username' => 'datetest2')); + $this->assertTrue(isset($test['createdAt'])); + + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'datetest2')); + $this->assertTrue($user->getCreatedAt() instanceof \DateTime); + $this->assertEquals('1900-01-01', $user->getCreatedAt()->format('Y-m-d')); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DetachedDocumentTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DetachedDocumentTest.php new file mode 100644 index 00000000..decafc45 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/DetachedDocumentTest.php @@ -0,0 +1,128 @@ +name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + // $user is now detached + + $this->assertFalse($this->dm->contains($user)); + + $user->name = 'Roman B.'; + + //$this->assertEquals(UnitOfWork::STATE_DETACHED, $this->dm->getUnitOfWork()->getEntityState($user)); + + $user2 = $this->dm->merge($user); + + $this->assertFalse($user === $user2); + $this->assertTrue($this->dm->contains($user2)); + $this->assertEquals('Roman B.', $user2->name); + } + + public function testSerializeUnserializeModifyMerge() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $ph1 = new CmsPhonenumber; + $ph1->phonenumber = 1234; + $user->addPhonenumber($ph1); + + $this->dm->persist($user); + $this->dm->flush(); + $this->assertTrue($this->dm->contains($user)); + $this->assertTrue($user->phonenumbers->isInitialized()); + + $serialized = serialize($user); + + $this->dm->clear(); + $this->assertFalse($this->dm->contains($user)); + unset($user); + + $user = unserialize($serialized); + + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = 56789; + $user->addPhonenumber($ph2); + $this->assertEquals(2, count($user->getPhonenumbers())); + $this->assertFalse($this->dm->contains($user)); + + $this->dm->persist($ph2); + + // Merge back in + $user = $this->dm->merge($user); // merge cascaded to phonenumbers + $this->dm->flush(); + + $this->assertTrue($this->dm->contains($user)); + $this->assertEquals(2, count($user->getPhonenumbers())); + $phonenumbers = $user->getPhonenumbers(); + $this->assertTrue($this->dm->contains($phonenumbers[0])); + $this->assertTrue($this->dm->contains($phonenumbers[1])); + } + + /** + * @group DDC-203 + */ + public function testDetachedEntityThrowsExceptionOnFlush() + { + $ph = new CmsPhonenumber(); + $ph->phonenumber = '12345'; + $this->dm->persist($ph); + $this->dm->flush(); + $this->dm->clear(); + $this->dm->persist($ph); + try { + $this->dm->flush(); + $this->fail(); + } catch (\Exception $expected) {} + } + + public function testUninitializedLazyAssociationsAreIgnoredOnMerge() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $address = new CmsAddress; + $address->city = 'Berlin'; + $address->country = 'Germany'; + $address->street = 'Sesamestreet'; + $address->zip = 12345; + $address->setUser($user); + $this->dm->persist($address); + $this->dm->persist($user); + + $this->dm->flush(); + $this->dm->clear(); + + $address2 = $this->dm->find(get_class($address), $address->id); + $this->assertTrue($address2->user instanceof \Doctrine\ODM\MongoDB\Proxy\Proxy); + $this->assertFalse($address2->user->__isInitialized__); + $detachedAddress2 = unserialize(serialize($address2)); + $this->assertTrue($detachedAddress2->user instanceof \Doctrine\ODM\MongoDB\Proxy\Proxy); + $this->assertFalse($detachedAddress2->user->__isInitialized__); + + $managedAddress2 = $this->dm->merge($detachedAddress2); + $this->assertTrue($managedAddress2->user instanceof \Doctrine\ODM\MongoDB\Proxy\Proxy); + $this->assertFalse($managedAddress2->user === $detachedAddress2->user); + $this->assertFalse($managedAddress2->user->__isInitialized__); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EagerCursorTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EagerCursorTest.php new file mode 100644 index 00000000..87941eef --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EagerCursorTest.php @@ -0,0 +1,82 @@ + 'test'); + $this->dm->getDocumentCollection('Doctrine\ODM\MongoDB\Tests\Functional\EagerTestDocument')->insert($document); + + $this->document = new EagerTestDocument(); + $this->document->id = (string) $document['_id']; + $this->document->test = 'test'; + + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\EagerTestDocument'); + $qb->eagerCursor(true); + $this->test = $qb->getQuery()->execute(); + } + + public function testEagerCursor() + { + $this->assertInstanceOf('Doctrine\ODM\MongoDB\EagerCursor', $this->test); + } + + public function testIsInitialized() + { + $this->assertFalse($this->test->isInitialized()); + $this->test->initialize(); + $this->assertTrue($this->test->isInitialized()); + } + + public function testCount() + { + $this->assertEquals(1, count($this->test)); + } + + public function testGetSingleResult() + { + $this->assertEquals($this->document, $this->test->getSingleResult()); + } + + public function testToArray() + { + $this->assertEquals(array($this->document->id => $this->document), $this->test->toArray()); + } + + public function testHydrate() + { + $this->test->hydrate(false); + $this->assertTrue(is_array($this->test->getSingleResult())); + + $this->test->hydrate(true); + $this->assertTrue(is_object($this->test->getSingleResult())); + } + + public function testRewind() + { + $this->test->toArray(); + $this->assertFalse($this->test->next()); + $this->test->rewind(); + $this->assertEquals($this->document, $this->test->current()); + $this->assertFalse($this->test->next()); + } +} + +/** @ODM\Document */ +class EagerTestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $test; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EcommerceTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EcommerceTest.php new file mode 100644 index 00000000..8ccf1cee --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EcommerceTest.php @@ -0,0 +1,133 @@ +setProxyDir(__DIR__ . '/../../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $reader = new AnnotationReader(); + $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents')); + + $this->dm = DocumentManager::create(new Connection(), $config); + + $currencies = array('USD' => 1, 'EURO' => 1.7, 'JPN' => 0.0125); + + foreach ($currencies as $name => &$multiplier) { + $multiplier = new Currency($name, $multiplier); + $this->dm->persist($multiplier); + } + + $product = new ConfigurableProduct('T-Shirt'); + $product->addOption(new Option('small', new Money(12.99, $currencies['USD']), new StockItem('T-shirt Size S', new Money(9.99, $currencies['USD']), 15))); + $product->addOption(new Option('medium', new Money(14.99, $currencies['USD']), new StockItem('T-shirt Size M', new Money(11.99, $currencies['USD']), 15))); + $product->addOption(new Option('large', new Money(17.99, $currencies['USD']), new StockItem('T-shirt Size L', new Money(13.99, $currencies['USD']), 15))); + + $this->dm->persist($product); + $this->dm->flush(); + foreach ($currencies as $currency) { + $this->dm->detach($currency); + } + $this->dm->detach($product); + + unset($currencies, $product); + } + + public function tearDown() + { + $documents = array( + 'Documents\Ecommerce\ConfigurableProduct', + 'Documents\Ecommerce\StockItem', + 'Documents\Ecommerce\Currency', + ); + foreach ($documents as $document) { + $this->dm->getDocumentCollection($document)->drop(); + } + } + + public function testEmbedding() + { + $product = $this->getProduct(); + $price = $product->getOption('small')->getPrice(true); + $currency = $price->getCurrency(); + $this->assertTrue($currency instanceof Currency); + $this->assertEquals(3, count($product->getOptions())); + $this->assertEquals(12.99, $product->getOption('small')->getPrice()); + + $usdCurrency = $this->dm->getRepository('Documents\Ecommerce\Currency')->findOneBy(array('name' => 'USD')); + $this->assertNotNull($usdCurrency); + $usdCurrency->setMultiplier('2'); + + $this->assertTrue($product->getOption('small')->getStockItem() instanceof \Documents\Ecommerce\StockItem); + $this->assertNotNull($product->getOption('small')->getStockItem()->getId()); + $this->assertEquals(12.99 * 2, $product->getOption('small')->getPrice()); + } + + public function testMoneyDocumentsAvailableForReference() + { + $product = $this->getProduct(); + $price = $product->getOption('small')->getPrice(true); + $currency = $price->getCurrency(); + $this->assertTrue($currency instanceof Currency); + $this->assertNotNull($currency->getId()); + $this->assertEquals($currency, $this->dm->getRepository('Documents\Ecommerce\Currency')->findOneBy(array('name' => Currency::USD))); + } + + public function testRemoveOption() + { + $product = $this->getProduct(); + + $this->assertEquals(3, count($product->getOptions())); + $product->removeOption('small'); + $this->assertEquals(2, count($product->getOptions())); + $this->dm->flush(); + $this->dm->detach($product); + unset($product); + $this->assertFalse(isset($product)); + + $product = $this->getProduct(); + $this->assertEquals(2, count($product->getOptions())); + } + + public function testDoesNotSaveTransientFields() + { + $product = $this->getProduct(); + + $product->selectOption('small'); + $this->dm->flush(); + } + + protected function getProduct() + { + $products = $this->dm->getRepository('Documents\Ecommerce\ConfigurableProduct') + ->createQueryBuilder() + ->getQuery() + ->execute(); + $products->valid() ?: $products->next(); + return $products->current(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedReferenceTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedReferenceTest.php new file mode 100644 index 00000000..31f3394f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedReferenceTest.php @@ -0,0 +1,109 @@ +links->add($link1); + /* END ADD EMBEDDED DOCUMENT + + /* ADD REFERENCED DOCUMENTS TO EMBEDDED DOCUMENT */ + $referencedDocument1 = new ReferencedDocument('Referenced Document 1'); + $this->dm->persist($referencedDocument1); + $link1->referencedDocuments->add($referencedDocument1); + + $referencedDocument2 = new ReferencedDocument('Referenced Document 2'); + $this->dm->persist($referencedDocument2); + $link1->referencedDocuments->add($referencedDocument2); + + $referencedDocument3 = new ReferencedDocument('Referenced Document 3'); + $this->dm->persist($referencedDocument3); + $link1->referencedDocuments->add($referencedDocument3); + + $referencedDocument4 = new ReferencedDocument('Referenced Document 4'); + $this->dm->persist($referencedDocument4); + $link1->referencedDocuments->add($referencedDocument4); + + $referencedDocument5 = new ReferencedDocument('Referenced Document 5'); + $this->dm->persist($referencedDocument5); + $link1->referencedDocuments->add($referencedDocument5); + /* END ADD REFERENCED DOCUMENTS TO EMBEDDED DOCUMENT */ + + // persist & flush + $this->dm->persist($offer); + $this->dm->flush(); + $this->dm->clear(); + + $offer = $this->dm->getRepository(__NAMESPACE__ . '\Offer')->findOneByName('My Offer'); + + // Should be: 1 Link, 5 referenced documents + // Actual Result: 1 link, 10 referenced documents + $this->assertEquals(1, $offer->links->count()); + $this->assertEquals(5, $offer->links[0]->referencedDocuments->count()); + } +} + +/** @ODM\Document */ +class Offer +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="Link") */ + public $links; + + public function __construct($name) + { + $this->name = $name; + $this->links = new ArrayCollection(); + } +} + +/** @ODM\EmbeddedDocument */ +class Link +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $url; + + /** @ODM\ReferenceMany(targetDocument="ReferencedDocument") */ + public $referencedDocuments; + + public function __construct($url) + { + $this->url = $url; + $this->referencedDocuments = new ArrayCollection(); + } +} + +/** @ODM\Document */ +class ReferencedDocument +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php new file mode 100644 index 00000000..bdae08ac --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php @@ -0,0 +1,461 @@ +setId((string) new \MongoId()); + $user->setUsername('jwage'); + $user->setAddress(null); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + $userId = $user->getId(); + + $user = $this->dm->getRepository('Documents\User')->find($userId); + $this->assertEquals($userId, $user->getId()); + $this->assertNull($user->getAddress()); + } + + public function testFlushEmbedded() + { + $test = new EmbeddedTestLevel0(); + $test->name = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository('Documents\Functional\EmbeddedTestLevel0')->findOneBy(array('name' => 'test')); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel0', $test); + + // Adding this flush here makes level1 not to be inserted. + $this->dm->flush(); + + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->level1[] = $level1; + + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find('Documents\Functional\EmbeddedTestLevel0', $test->id); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel0', $test); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel1', $test->level1[0]); + + $test->level1[0]->name = 'changed'; + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'testing'; + $test->level1->add($level1); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find('Documents\Functional\EmbeddedTestLevel0', $test->id); + $this->assertEquals(2, count($test->level1)); + $this->assertEquals('changed', $test->level1[0]->name); + $this->assertEquals('testing', $test->level1[1]->name); + + unset($test->level1[0]); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertEquals(1, count($test->level1)); + } + + public function testOneEmbedded() + { + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + $address->setCity('Nashville'); + $address->setState('TN'); + $address->setZipcode('37209'); + + $addressClone = clone $address; + + $user = new User(); + $user->setUsername('jwage'); + + $this->dm->persist($user); + $this->dm->flush(); + + $user->setAddress($address); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()) + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($user); + $this->assertEquals($addressClone, $user->getAddress()); + } + + public function testRemoveOneEmbedded() + { + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + + $user = new User(); + $user->setUsername('jwage'); + $user->setAddress($address); + + $this->dm->persist($user); + $this->dm->flush(); + + $user->removeAddress(); + $this->assertNull($user->getAddress()); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()) + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($user); + $this->assertNull($user->getAddress()); + } + + public function testManyEmbedded() + { + $user = new \Documents\User(); + $user->addPhonenumber(new Phonenumber('6155139185')); + $user->addPhonenumber(new Phonenumber('6153303769')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user2 = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()) + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($user2); + $this->assertEquals($user->getPhonenumbers()->toArray(), $user2->getPhonenumbers()->toArray()); + } + + public function testPostRemoveEventOnEmbeddedManyDocument() + { + // create a test document + $test = new EmbeddedTestLevel0b(); + $test->name = 'embedded test'; + + // embed one level1 in test + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->level1[] = $level1; + + // persist test + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + $level1 = $test->level1[0]; + + // $test->level1[0] is available + $this->assertEquals('test level1 #1', $level1->name); + + // remove all level1 from test + $test->level1->clear(); + $this->dm->flush(); + + // verify that level1 lifecycle callbacks have been called + $this->assertTrue($level1->preRemove, 'the removed embedded document executed the PreRemove lifecycle callback'); + $this->assertTrue($level1->postRemove, 'the removed embedded document executed the PostRemove lifecycle callback'); + } + + public function testRemoveEmbeddedManyDocument() + { + // create a test document + $test = new EmbeddedTestLevel0b(); + $test->name = 'embedded test'; + + // embed one level1 in test + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->level1[] = $level1; + + // persist test + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + + // $test->level1[0] is available + $this->assertEquals('test level1 #1', $test->level1[0]->name); + + // remove all level1 from test + $test->level1->clear(); + $this->dm->flush(); + $this->dm->clear(); + + // verify that test has no more level1 + $this->assertEquals(0, $test->level1->count()); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + + $this->assertInstanceOf('Doctrine\ODM\MongoDB\PersistentCollection', $test->level1); + + // verify that test has no more level1 + $this->assertEquals(0, $test->level1->count()); + } + + public function testRemoveDeepEmbeddedManyDocument() + { + // create a test document + $test = new EmbeddedTestLevel0b(); + $test->name = 'embedded test'; + + // embed one level1 in test + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->oneLevel1 = $level1; + + // embed one level2 in level1 + $level2 = new EmbeddedTestLevel2(); + $level2->name = 'test level2 #1'; + $level1->level2[] = $level2; + + // persist test + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + $level1 = $test->oneLevel1; + $level2 = $level1->level2[0]; + + // $test->oneLevel1->level2[0] is available + $this->assertEquals('test level2 #1', $level2->name); + + // remove all level2 from level1 + $level1->level2->clear(); + $this->dm->flush(); + $this->dm->clear(); + + // verify that level1 has no more level2 + $this->assertEquals(0, $level1->level2->count()); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + $level1 = $test->oneLevel1; + + // verify that level1 has no more level2 + $this->assertEquals(0, $level1->level2->count()); + } + + public function testPostRemoveEventOnDeepEmbeddedManyDocument() + { + // create a test document + $test = new EmbeddedTestLevel0b(); + $test->name = 'embedded test'; + + // embed one level1 in test + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->oneLevel1 = $level1; + + // embed one level2 in level1 + $level2 = new EmbeddedTestLevel2(); + $level2->name = 'test level2 #1'; + $level1->level2[] = $level2; + + // persist test + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + // retrieve test + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + $level1 = $test->oneLevel1; + $level2 = $level1->level2[0]; + + // $test->oneLevel1->level2[0] is available + $this->assertEquals('test level2 #1', $level2->name); + + // remove all level2 from level1 + $level1->level2->clear(); + $this->dm->flush(); + + // verify that level2 lifecycle callbacks have been called + $this->assertTrue($level2->preRemove, 'the removed embedded document executed the PreRemove lifecycle callback'); + $this->assertTrue($level2->postRemove, 'the removed embedded document executed the PostRemove lifecycle callback'); + } + + public function testEmbeddedLoadEvents() + { + // create a test document + $test = new EmbeddedTestLevel0b(); + $test->name = 'embedded test'; + + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'test level1 #1'; + $test->oneLevel1 = $level1; + + $level2 = new EmbeddedTestLevel2(); + $level2->name = 'test level2 #1'; + $level1->level2[] = $level2; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->createQueryBuilder(get_class($test)) + ->field('id')->equals($test->id) + ->getQuery() + ->getSingleResult(); + $level1 = $test->oneLevel1; + $level2 = $level1->level2[0]; + + $this->assertTrue($level1->preLoad); + $this->assertTrue($level1->postLoad); + $this->assertTrue($level2->preLoad); + $this->assertTrue($level2->postLoad); + } + + public function testEmbeddedDocumentChangesParent() + { + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + $user = new User(); + $user->setUsername('jwagettt'); + $user->setAddress($address); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertNotNull($user); + $address = $user->getAddress(); + $address->setAddress('changed'); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertEquals('changed', $user->getAddress()->getAddress()); + } + + public function testRemoveEmbeddedDocument() + { + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + $user = new User(); + $user->setUsername('jwagettt'); + $user->setAddress($address); + $user->addPhonenumber(new Phonenumber('6155139185')); + $user->addPhonenumber(new Phonenumber('6155139185')); + + $this->dm->persist($user); + $this->dm->flush(); + + $user->removeAddress(); + + $user->getPhonenumbers()->remove(0); + $user->getPhonenumbers()->remove(1); + + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection('Documents\User')->findOne(); + $this->assertEmpty($check['phonenumbers']); + $this->assertFalse(isset($check['address'])); + } + + public function testRemoveAddDeepEmbedded() + { + $vhost = new VirtualHost(); + + $directive1 = new VirtualHostDirective('DirectoryIndex', 'index.php'); + $vhost->getVHostDirective()->addDirective($directive1); + + $directive2 = new VirtualHostDirective('Directory', '/var/www/html'); + $directive2->addDirective(new VirtualHostDirective('AllowOverride','All')); + $vhost->getVHostDirective()->addDirective($directive2); + + $directive3 = new VirtualHostDirective('Directory', '/var/www/html'); + $directive3->addDirective(new VirtualHostDirective('RewriteEngine','on')); + $vhost->getVHostDirective()->addDirective($directive3); + + $this->dm->persist($vhost); + $this->dm->flush(); + + $vhost->getVHostDirective()->removeDirective($directive2); + + $directive4 = new VirtualHostDirective('Directory', '/var/www/html'); + $directive4->addDirective(new VirtualHostDirective('RewriteEngine','on')); + $vhost->getVHostDirective()->addDirective($directive4); + + + $this->dm->flush(); + $this->dm->clear(); + + $vhost = $this->dm->find('Documents\Functional\VirtualHost', $vhost->getId()); + + foreach($vhost->getVHostDirective()->getDirectives() as $directive) + { + $this->assertNotEmpty($directive->getName()); + } + } + + public function testEmbeddedDocumentNotSavedFields() + { + $document = new NotSaved(); + $document->embedded = new NotSavedEmbedded(); + $document->embedded->name = 'foo'; + $document->embedded->notSaved = 'bar'; + + $this->dm->persist($document); + $this->dm->flush(); + $this->dm->clear(); + + $document = $this->dm->find('Documents\Functional\NotSaved', $document->id); + + $this->assertEquals('foo', $document->embedded->name); + $this->assertNull($document->embedded->notSaved); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilesTest.php new file mode 100644 index 00000000..9f8f2987 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilesTest.php @@ -0,0 +1,164 @@ +setName('Test'); + $image->setFile(__DIR__ . '/file.txt'); + $this->dm->persist($image); + $this->dm->flush(); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\File')->findOne(); + $this->assertFalse(isset($test->file['file'])); + } + + public function testFiles() + { + $image = new File(); + $image->setName('Test'); + $image->setFile(__DIR__ . '/file.txt'); + + $profile = new Profile(); + $profile->setFirstName('Jon'); + $profile->setLastName('Wage'); + $profile->setImage($image); + + $this->dm->persist($profile); + $this->dm->flush(); + + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $image->getFile()); + $this->assertFalse($image->getFile()->isDirty()); + $this->assertEquals(__DIR__ . '/file.txt', $image->getFile()->getFilename()); + $this->assertTrue(file_exists($image->getFile()->getFilename())); + $this->assertEquals('These are the bytes...', $image->getFile()->getBytes()); + + $image->setName('testing'); + $this->dm->flush(); + $this->dm->clear(); + + $image = $this->dm->find('Documents\File', $image->getId()); + $this->assertNotNull($image); + $this->assertEquals('testing', $image->getName()); + $this->assertEquals('These are the bytes...', $image->getFile()->getBytes()); + } + + public function testFileReferences() + { + $image = new TestFile(); + $image->name = 'Test'; + $image->theFile = __DIR__ . '/file.txt'; + + $profile = new Profile(); + + $image->profiles[] = $profile; + + $this->dm->persist($profile); + $this->dm->persist($image); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(get_class($image))->findOne(); + $this->assertFalse(isset($check['$pushAll'])); + $this->assertTrue(isset($check['profiles'])); + $this->assertEquals(1, count($check['profiles'])); + $this->assertEquals($profile->getProfileId(), (string) $check['profiles'][0]['$id']); + } + + public function testCreateFileWithMongoGridFSFileObject() + { + $file = new \Doctrine\MongoDB\GridFSFile(__DIR__ . '/file.txt'); + + $image = new File(); + $image->setName('Test'); + $image->setFile($file); + + $profile = new Profile(); + $profile->setFirstName('Jon'); + $profile->setLastName('Wage'); + $profile->setImage($image); + + $this->assertTrue($image->getFile()->isDirty()); + + $this->dm->persist($profile); + $this->dm->flush(); + + $this->assertFalse($image->getFile()->isDirty()); + $this->assertSame($file, $image->getFile()); + + $this->dm->clear(); + + $profile = $this->dm->createQueryBuilder('Documents\Profile') + ->getQuery() + ->getSingleResult(); + $image = $profile->getImage(); + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $image->getFile()); + $this->assertEquals('These are the bytes...', $image->getFile()->getBytes()); + $image->getFile()->setFilename(__DIR__ . '/FilesTest.php'); + $this->dm->flush(); + $this->dm->clear(); + + $profile = $this->dm->createQueryBuilder('Documents\Profile') + ->getQuery() + ->getSingleResult(); + $image = $profile->getImage(); + $this->assertEquals('Test', $image->getName()); + $this->assertEquals(__DIR__ . '/FilesTest.php', $image->getFile()->getFilename()); + $this->assertEquals(file_get_contents(__DIR__ . '/FilesTest.php'), $image->getFile()->getBytes()); + + $image->getFile()->setBytes('test'); + $this->dm->flush(); + $this->dm->clear(); + + $profile = $this->dm->createQueryBuilder('Documents\Profile') + ->getQuery() + ->getSingleResult(); + $image = $profile->getImage(); + $this->assertEquals('test', $image->getFile()->getBytes()); + } + + public function testFileWithOtherNameThanFile() + { + $path = __DIR__.'/FilesTest.php'; + + $test = new TestFile(); + $test->name = 'Test'; + $test->theFile = new \Doctrine\MongoDB\GridFSFIle($path); + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository(__NAMESPACE__.'\TestFile')->find($test->id); + $this->assertNotNull($test); + $this->assertEquals(file_get_contents($path), $test->theFile->getBytes()); + } + + public function testFilesEmptyQueryReturnsNull() + { + $this->assertNull($this->dm->find('Documents\File', 'definitelynotanid')); + } +} + +/** @ODM\Document */ +class TestFile +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\File */ + public $theFile; + + /** @ODM\ReferenceMany(targetDocument="Documents\Profile") */ + public $profiles = array(); +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php new file mode 100644 index 00000000..c9585a74 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php @@ -0,0 +1,292 @@ +ids = array(); + + $groupA = new Group('groupA'); + $groupB = new Group('groupB'); + + $profile = new Profile(); + $profile->setFirstname('Timothy'); + + $tim = new User(); + $tim->setUsername('Tim'); + $tim->setHits(10); + $tim->addGroup($groupA); + $tim->addGroup($groupB); + $tim->setProfile($profile); + $this->dm->persist($tim); + + $john = new User(); + $john->setUsername('John'); + $john->setHits(10); + $this->dm->persist($john); + + $this->dm->flush(); + $this->dm->clear(); + + $this->ids['tim'] = $tim->getId(); + $this->ids['john'] = $john->getId(); + + $this->fc = $this->dm->getFilterCollection(); + } + + protected function enableUserFilter() + { + $this->fc->enable('testFilter'); + $testFilter = $this->fc->getFilter('testFilter'); + $testFilter->setParameter('class', 'Documents\User'); + $testFilter->setParameter('field', 'username'); + $testFilter->setParameter('value', 'Tim'); + } + + protected function enableGroupFilter() + { + $this->fc->enable('testFilter'); + $testFilter = $this->fc->getFilter('testFilter'); + $testFilter->setParameter('class', 'Documents\Group'); + $testFilter->setParameter('field', 'name'); + $testFilter->setParameter('value', 'groupA'); + } + + protected function enableProfileFilter() + { + $this->fc->enable('testFilter'); + $testFilter = $this->fc->getFilter('testFilter'); + $testFilter->setParameter('class', 'Documents\Profile'); + $testFilter->setParameter('field', 'firstname'); + $testFilter->setParameter('value', 'Something Else'); + } + + public function testRepositoryFind() + { + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFind()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(array('Tim'), $this->getUsernamesWithFind()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFind()); + } + + protected function getUsernamesWithFind() + { + $repository = $this->dm->getRepository('Documents\User'); + + $tim = $repository->find($this->ids['tim']); + $john = $repository->find($this->ids['john']); + + $usernames = array(); + + if(isset($tim)) { + $usernames[] = $tim->getUsername(); + } + if(isset($john)) { + $usernames[] = $john->getUsername(); + } + + sort($usernames); + return $usernames; + } + + public function testRepositoryFindBy() + { + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFindBy()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(array('Tim'), $this->getUsernamesWithFindBy()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFindBy()); + } + + protected function getUsernamesWithFindBy() + { + $all = $this->dm->getRepository('Documents\User')->findBy(array('hits' => 10)); + + $usernames = array(); + foreach ($all as $user) { + $usernames[] = $user->getUsername(); + } + sort($usernames); + return $usernames; + } + + public function testRepositoryFindOneBy() + { + $this->assertEquals('John', $this->getJohnsUsernameWithFindOneBy()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(null, $this->getJohnsUsernameWithFindOneBy()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals('John', $this->getJohnsUsernameWithFindOneBy()); + } + + protected function getJohnsUsernameWithFindOneBy() + { + $john = $this->dm->getRepository('Documents\User')->findOneBy(array('id' => $this->ids['john'])); + + return isset($john) ? $john->getUsername() : null; + } + + public function testRepositoryFindAll() + { + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFindAll()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(array('Tim'), $this->getUsernamesWithFindAll()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithFindAll()); + } + + protected function getUsernamesWithFindAll() + { + $all = $this->dm->getRepository('Documents\User')->findAll(); + + $usernames = array(); + foreach ($all as $user) { + $usernames[] = $user->getUsername(); + } + sort($usernames); + return $usernames; + } + + public function testReferenceMany() + { + $this->assertEquals(array('groupA', 'groupB'), $this->getGroupsByReference()); + + $this->enableGroupFilter(); + $this->dm->clear(); + $this->assertEquals(array('groupA'), $this->getGroupsByReference()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('groupA', 'groupB'), $this->getGroupsByReference()); + } + + protected function getGroupsByReference() + { + $tim = $this->dm->getRepository('Documents\User')->find($this->ids['tim']); + + $groupnames = array(); + foreach ($tim->getGroups() as $group) { + try { + $groupnames[] = $group->getName(); + } catch (\Doctrine\ODM\MongoDB\DocumentNotFoundException $e) { + //Proxy object filtered + } + } + sort($groupnames); + return $groupnames; + } + + public function testReferenceOne() + { + $this->assertEquals('Timothy', $this->getProfileByReference()); + + $this->enableProfileFilter(); + $this->dm->clear(); + $this->assertEquals(null, $this->getProfileByReference()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals('Timothy', $this->getProfileByReference()); + } + + protected function getProfileByReference() + { + $tim = $this->dm->getRepository('Documents\User')->find($this->ids['tim']); + + $profile = $tim->getProfile(); + try { + return $profile->getFirstname(); + } catch (\Doctrine\ODM\MongoDB\DocumentNotFoundException $e) { + //Proxy object filtered + return null; + } + } + + public function testDocumentManagerRef() + { + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithDocumentManager()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(array('Tim'), $this->getUsernamesWithDocumentManager()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithDocumentManager()); + } + + protected function getUsernamesWithDocumentManager() + { + $tim = $this->dm->getReference('Documents\User', $this->ids['tim']); + $john = $this->dm->getReference('Documents\User', $this->ids['john']); + + $usernames = array(); + + try { + $usernames[] = $tim->getUsername(); + } catch (\Doctrine\ODM\MongoDB\DocumentNotFoundException $e) { + //Proxy object filtered + } + + try { + $usernames[] = $john->getUsername(); + } catch (\Doctrine\ODM\MongoDB\DocumentNotFoundException $e) { + //Proxy object filtered + } + + sort($usernames); + return $usernames; + } + + public function testQuery() + { + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithQuery()); + + $this->enableUserFilter(); + $this->dm->clear(); + $this->assertEquals(array('Tim'), $this->getUsernamesWithQuery()); + + $this->fc->disable('testFilter'); + $this->dm->clear(); + $this->assertEquals(array('John', 'Tim'), $this->getUsernamesWithQuery()); + } + + protected function getUsernamesWithQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User'); + $query = $qb->getQuery(); + $all = $query->execute(); + + $usernames = array(); + foreach ($all as $user) { + $usernames[] = $user->getUsername(); + } + sort($usernames); + return $usernames; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FindAndModifyTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FindAndModifyTest.php new file mode 100644 index 00000000..62c5d73a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FindAndModifyTest.php @@ -0,0 +1,62 @@ +dm->getDocumentCollection('Documents\User'); + $docs = array(array('count' => 0), array('count' => 0)); + $coll->batchInsert($docs); + + // test update findAndModify + $q = $this->dm->createQueryBuilder() + ->findAndUpdate('Documents\User') + ->returnNew(true) + ->field('count')->inc(5) + ->field('username')->set('jwage') + ->getQuery(); + $result = $q->execute(); + + // Test the username was set and count incremented + $this->assertEquals('jwage', $result->getUsername()); + $this->assertEquals(5, $result->getCount()); + + // Test remove findAndModify + $q = $this->dm->createQueryBuilder() + ->findAndRemove('Documents\User') + ->field('username')->equals('jwage') + ->getQuery(); + $result = $q->execute(); + + // Test the object was returned + $this->assertEquals('jwage', $result->getUsername()); + + // Test the object was removed + $this->assertEquals(1, $this->dm->getDocumentCollection('Documents\User')->find()->count()); + } + + public function testFindAndModifyAlt() + { + $doc = new User(); + $doc->setUsername('jwage'); + + $this->dm->persist($doc); + $this->dm->flush(); + + // test update findAndModify + $q = $this->dm->createQueryBuilder() + ->findAndUpdate('Documents\User') + ->returnNew(true) + ->field('username')->equals('jwage') + ->field('username')->set('Romain Neutron') + ->getQuery(); + $result = $q->execute(); + + // Test the username was set + $this->assertEquals('Romain Neutron', $result->getUsername()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushOptionsTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushOptionsTest.php new file mode 100644 index 00000000..62f7da8e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushOptionsTest.php @@ -0,0 +1,19 @@ +setUsername('jwage'); + $this->dm->persist($user); + $this->dm->flush(null, array('safe' => true)); + + $user->setUsername('ok'); + $this->dm->flush(null, array('safe' => true)); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php new file mode 100644 index 00000000..bf252cbe --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php @@ -0,0 +1,180 @@ +addFriend($userB); + $userB->addFriend($userC); + + // persist all users, flush and clear + foreach (array($userA, $userB, $userC) as $user) $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertSize(0); + + $userA = $this->dm->find(get_class($userA), $userA->id); + + // the size is 1. userA is in the uow. + $this->assertSize(1); + + // first flush + $this->dm->flush(); + + // now the size is 2! userA and userB are in the UOW. + $this->assertSize(1); + + // second flush + $this->dm->flush(); + + // now the size is 3! userA and userB and userC are in the UOW. + $this->assertSize(1); + } + + public function testFlushManyExplicitDocuments() + { + $userA = new FriendUser('userA'); + $userB = new FriendUser('userB'); + $userC = new FriendUser('userC'); + + $this->dm->persist($userA); + $this->dm->persist($userB); + $this->dm->persist($userC); + + $this->dm->flush(array($userA, $userB, $userC)); + + $this->assertNotNull($userA->id); + $this->assertNotNull($userB->id); + $this->assertNotNull($userC->id); + } + + public function testFlushSingleManagedDocument() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $this->dm->persist($user); + $this->dm->flush(); + + $user->status = 'administrator'; + $this->dm->flush($user); + $this->dm->clear(); + + $user = $this->dm->find(get_class($user), $user->id); + $this->assertEquals('administrator', $user->status); + } + + public function testFlushSingleUnmanagedDocument() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $this->setExpectedException('InvalidArgumentException', 'Document has to be managed for single computation'); + $this->dm->flush($user); + } + + public function testFlushSingleAndNewDocument() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $this->dm->persist($user); + $this->dm->flush(); + + $otherUser = new CmsUser; + $otherUser->name = 'Dominik2'; + $otherUser->username = 'domnikl2'; + $otherUser->status = 'developer'; + + $user->status = 'administrator'; + + $this->dm->persist($otherUser); + $this->dm->flush($user); + + $this->assertTrue($this->dm->contains($otherUser), "Other user is contained in DocumentManager"); + $this->assertTrue($otherUser->id > 0, "other user has an id"); + } + + public function testFlushAndCascadePersist() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $this->dm->persist($user); + $this->dm->flush(); + + $address = new CmsAddress(); + $address->city = "Springfield"; + $address->zip = "12354"; + $address->country = "Germany"; + $address->street = "Foo Street"; + $address->user = $user; + $user->address = $address; + + $this->dm->flush($user); + + $this->assertTrue($this->dm->contains($address), "Other user is contained in DocumentManager"); + $this->assertTrue($address->id > 0, "other user has an id"); + } + + public function testProxyIsIgnored() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getReference(get_class($user), $user->id); + + $otherUser = new CmsUser; + $otherUser->name = 'Dominik2'; + $otherUser->username = 'domnikl2'; + $otherUser->status = 'developer'; + + $this->dm->persist($otherUser); + $this->dm->flush($user); + + $this->assertTrue($this->dm->contains($otherUser), "Other user is contained in DocumentManager"); + $this->assertTrue($otherUser->id > 0, "other user has an id"); + } + + protected function assertSize($size) + { + $this->assertEquals($size, $this->dm->getUnitOfWork()->size()); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php new file mode 100644 index 00000000..ce0a1c19 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php @@ -0,0 +1,899 @@ +id = (string) $id; + $user->username = 'test'; + $user->count = 1; + $group = new \Documents\Group('Group'); + $user->groups = array($group); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getDocumentCollection($className)->findOne(array('_id' => $id)); + $this->assertNotNull($check); + $this->assertEquals((string) $id, (string) $check['_id']); + $this->assertEquals($group->getId(), (string) $check['groups'][0]['$id']); + $this->assertEquals($discriminator, $check['discriminator']); + + $group2 = new \Documents\Group('Group'); + + $user = new $className(); + $user->id = $id; + $user->hits = 5; + $user->count = 2; + $user->groups = array($group2); + $this->dm->persist($user); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection($className)->findOne(array('_id' => $id)); + $this->assertEquals($discriminator, $check['discriminator']); + $this->assertEquals(3, $check['count']); + $this->assertEquals(5, $check['hits']); + $this->assertEquals(2, count($check['groups'])); + $this->assertEquals($group->getId(), (string) $check['groups'][0]['$id']); + $this->assertEquals($group2->getId(), (string) $check['groups'][1]['$id']); + $this->assertTrue(isset($check['username'])); + $this->assertEquals('test', $check['username']); + + $user = new $className(); + $user->id = $id; + $user->hits = 100; + $this->dm->persist($user); + $this->dm->flush(null, array('safe' => true)); + + $check = $this->dm->getDocumentCollection($className)->findOne(array('_id' => $id)); + $this->assertEquals($discriminator, $check['discriminator']); + $this->assertEquals(3, $check['count']); + $this->assertEquals(100, $check['hits']); + $this->assertEquals(2, count($check['groups'])); + $this->assertEquals($group->getId(), (string) $check['groups'][0]['$id']); + $this->assertEquals($group2->getId(), (string) $check['groups'][1]['$id']); + $this->assertTrue(isset($check['username'])); + $this->assertEquals('test', $check['username']); + } + + public function testFlushSingleDocument() + { + $user1 = new \Documents\ForumUser(); + $user1->username = 'romanb'; + $user2 = new \Documents\ForumUser(); + $user2->username = 'jwage'; + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->flush(); + + $user1->username = 'changed'; + $user2->username = 'changed'; + $this->dm->flush($user1); + + $check = $this->dm->getDocumentCollection('Documents\ForumUser')->find(array('username' => 'jwage')); + $this->assertNotNull($check); + + $check = $this->dm->getDocumentCollection('Documents\ForumUser')->find(array('username' => 'changed')); + $this->assertNotNull($check); + } + + public function testNestedCategories() + { + $root = new \Documents\Category('Root'); + $child1 = new \Documents\SubCategory('Child 1'); + $child2 = new \Documents\SubCategory('Child 2'); + $child1->addChild($child2); + $root->addChild($child1); + + $this->dm->persist($root); + $this->dm->flush(); + + $child1->setName('Child 1 Changed'); + $child2->setName('Child 2 Changed'); + $root->setName('Root Changed'); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Category')->findOne(); + $this->assertEquals('Child 1 Changed', $test['children'][0]['name']); + $this->assertEquals('Child 2 Changed', $test['children'][0]['children'][0]['name']); + $this->assertEquals('Root Changed', $test['name']); + } + + public function testNotSaved() + { + $test = new \Documents\Functional\AlsoLoad(); + $test->bar = 'test'; + $test->firstName = 'Jon'; + $this->dm->persist($test); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Functional\AlsoLoad')->findOne(array('firstName' => 'Jon')); + $this->assertEquals('Jon', $test['firstName']); + $this->assertFalse(isset($test['bar'])); + } + + public function testManyEmbedded() + { + $album = new Album('Jon'); + $album->addSong(new Song('Song #1')); + $album->addSong(new Song('Song #2')); + $this->dm->persist($album); + $this->dm->flush(); + + $songs = $album->getSongs(); + + $songs[0]->setName('Song #1 Changed'); + $songs->add(new Song('Song #3')); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Album')->findOne(array('name' => 'Jon')); + $this->assertEquals('Song #1 Changed', $test['songs'][0]['name']); + + $album->setName('jwage'); + $songs[1]->setName('ok'); + $songs->add(new Song('Song #4')); + $songs->add(new Song('Song #5')); + unset($songs[0]); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Album')->findOne(array('name' => 'jwage')); + + $this->assertEquals('jwage', $test['name']); + $this->assertEquals('ok', $test['songs'][0]['name']); + $this->assertEquals('Song #3', $test['songs'][1]['name']); + $this->assertEquals('Song #4', $test['songs'][2]['name']); + $this->assertEquals('Song #5', $test['songs'][3]['name']); + $this->assertEquals(4, count($test['songs'])); + + $songs->clear(); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection('Documents\Album')->findOne(array('name' => 'jwage')); + $this->assertFalse(isset($test['songs'])); + } + + public function testNewEmbedded() + { + $subAddress = new Address(); + $subAddress->setCity('Old Sub-City'); + + $address = new Address(); + $address->setCity('Old City'); + $address->setSubAddress($subAddress); + + $user = new Project('Project'); + $user->setAddress($address); + $this->dm->persist($user); + $this->dm->flush(); + + $address->setCity('New City'); + $subAddress->setCity('New Sub-City'); + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Project')->findOne(array('name' => 'Project')); + + $this->assertEquals('New Sub-City', $test['address']['subAddress']['city']); + $this->assertEquals('New City', $test['address']['city']); + } + + public function testPersistingNewDocumentWithOnlyOneReference() + { + $server = new \Documents\GuestServer(); + $server->name = 'test'; + $this->dm->persist($server); + $this->dm->flush(); + $id = $server->id; + + $this->dm->clear(); + + $server = $this->dm->getReference('Documents\GuestServer', $id); + + $agent = new \Documents\Agent(); + $agent->server = $server; + $this->dm->persist($agent); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection('Documents\Agent')->findOne(); + + $this->assertEquals('servers', $test['server']['$ref']); + $this->assertTrue(isset($test['server']['$id'])); + $this->assertEquals('doctrine_odm_tests', $test['server']['$db']); + $this->assertEquals('server_guest', $test['server']['_doctrine_class_name']); + } + + public function testCollection() + { + $user = new \Documents\User(); + $user->setUsername('joncolltest'); + $user->log(array('test')); + $user->log(array('test')); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $coll = $this->dm->getDocumentCollection('Documents\User'); + $document = $coll->findOne(array('username' => 'joncolltest')); + $this->assertEquals(2, count($document['logs'])); + + $document = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'joncolltest')); + $this->assertEquals(2, count($document->getLogs())); + $document->log(array('test')); + $this->dm->flush(); + $this->dm->clear(); + + $document = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'joncolltest')); + $this->assertEquals(3, count($document->getLogs())); + $document->setLogs(array('ok', 'test')); + $this->dm->flush(); + $this->dm->clear(); + + $document = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'joncolltest')); + $this->assertEquals(array('ok', 'test'), $document->getLogs()); + } + + public function testSameObjectValuesInCollection() + { + $user = new User(); + $user->setUsername('testing'); + $user->getPhonenumbers()->add(new Phonenumber('6155139185')); + $user->getPhonenumbers()->add(new Phonenumber('6155139185')); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'testing')); + $this->assertEquals(2, count($user->getPhonenumbers())); + } + + public function testIncrement() + { + $user = new User(); + $user->setUsername('jon'); + $user->setCount(100); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jon')); + + $user->incrementCount(5); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jon')); + $this->assertEquals(105, $user->getCount()); + + $user->setCount(50); + + $this->dm->flush(); + $this->dm->clear(); + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jon')); + $this->assertEquals(50, $user->getCount()); + } + + public function testTest() + { + $employee = new Employee(); + $employee->setName('Employee'); + $employee->setSalary(50000.00); + $employee->setStarted(new \DateTime()); + + $address = new Address(); + $address->setAddress('555 Doctrine Rd.'); + $address->setCity('Nashville'); + $address->setState('TN'); + $address->setZipcode('37209'); + $employee->setAddress($address); + + $project = new Project('New Project'); + $manager = new Manager(); + $manager->setName('Manager'); + $manager->setSalary(100000.00); + $manager->setStarted(new \DateTime()); + $manager->addProject($project); + + $this->dm->persist($employee); + $this->dm->persist($project); + $this->dm->persist($manager); + $this->dm->flush(); + + $newProject = new Project('Another Project'); + $manager->setSalary(200000.00); + $manager->addNote('Gave user 100k a year raise'); + $manager->incrementChanges(2); + $manager->addProject($newProject); + + $this->dm->persist($newProject); + $this->dm->flush(); + $this->dm->clear(); + + $result = $this->dm->createQueryBuilder('Documents\Manager') + ->field('name')->equals('Manager') + ->hydrate(false) + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(200000.00, $result['salary']); + $this->assertEquals(2, count($result['projects'])); + $this->assertEquals(1, count($result['notes'])); + $this->assertEquals('Gave user 100k a year raise', $result['notes'][0]); + } + + public function testNotAnnotatedDocument() + { + $this->dm->getDocumentCollection('Documents\Functional\NotAnnotatedDocument')->drop(); + + $test = new NotAnnotatedDocument(); + $test->field = 'test'; + $test->transientField = 'w00t'; + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find('Documents\Functional\NotAnnotatedDocument', $test->id); + $this->assertNotNull($test); + $this->assertFalse(isset($test->transientField)); + } + + public function testNullFieldValuesAllowed() + { + $this->dm->getDocumentCollection('Documents\Functional\NullFieldValues')->drop(); + + $test = new NullFieldValues(); + $test->field = null; + $this->dm->persist($test); + $this->dm->flush(); + + $document = $this->dm->createQueryBuilder('Documents\Functional\NullFieldValues') + ->hydrate(false) + ->getQuery() + ->getSingleResult(); + + $this->assertNotNull($document); + $this->assertNull($document['field']); + + $document = $this->dm->find('Documents\Functional\NullFieldValues', $test->id); + $document->field = 'test'; + $this->dm->flush(); + $this->dm->clear(); + + $document = $this->dm->find('Documents\Functional\NullFieldValues', $test->id); + $this->assertEquals('test', $document->field); + $document->field = null; + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->createQueryBuilder('Documents\Functional\NullFieldValues') + ->hydrate(false) + ->getQuery() + ->getSingleResult(); + $this->assertNull($test['field']); + $this->assertFalse(isset($test['transientField'])); + } + + public function testAlsoLoadOnProperty() + { + $collection = $this->dm->getDocumentCollection('Documents\Functional\AlsoLoad'); + $collection->drop(); + $doc = array( + 'bar' => 'w00t' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('bar' => 'w00t')); + $this->assertEquals('w00t', $document->foo); + + $doc = array( + 'foo' => 'cool' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('foo' => 'cool')); + $this->assertEquals('cool', $document->foo); + + $doc = array( + 'bar' => 'bar', + 'zip' => 'zip' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('zip' => 'zip')); + $this->assertEquals('bar', $document->foo); + $this->assertEquals('zip', $document->baz); + + $doc = array( + 'foo' => null, + 'bar' => 'nice' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('bar' => 'nice')); + $this->assertNull($document->foo); + + $doc = array( + 'bar' => null, + 'zip' => 'good' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('zip' => 'good')); + $this->assertNull($document->foo); + + $doc = array( + 'foo' => 'foo', + 'bar' => 'bar' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('foo' => 'foo')); + $this->assertEquals('foo', $document->foo); + } + + public function testAlsoLoadOnMethod() + { + $collection = $this->dm->getDocumentCollection('Documents\Functional\AlsoLoad'); + $collection->drop(); + $doc = array( + 'name' => 'Jonathan Wage', + 'test1' => 'test1' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('name' => 'Jonathan Wage')); + $this->assertEquals('Jonathan', $document->firstName); + $this->assertEquals('Wage', $document->lastName); + $this->assertEquals('test1', $document->test); + + $doc = array( + 'fullName' => 'Jonathan Wage', + 'test2' => 'test2' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('fullName' => 'Jonathan Wage')); + $this->assertEquals('Jonathan', $document->firstName); + $this->assertEquals('Wage', $document->lastName); + $this->assertEquals('test2', $document->test); + + $doc = array( + 'test' => 'test' + ); + $collection->insert($doc); + $document = $this->dm->getRepository('Documents\Functional\AlsoLoad')->findOneBy(array('test' => 'test')); + $this->assertEquals('test', $document->test); + } + + public function testSimplerEmbedAndReference() + { + $class = $this->dm->getClassMetadata('Documents\Functional\SimpleEmbedAndReference'); + $this->assertEquals('many', $class->fieldMappings['embedMany']['type']); + $this->assertEquals('one', $class->fieldMappings['embedOne']['type']); + $this->assertEquals('many', $class->fieldMappings['referenceMany']['type']); + $this->assertEquals('one', $class->fieldMappings['referenceOne']['type']); + } + + public function testNotSavedFields() + { + $collection = $this->dm->getDocumentCollection('Documents\Functional\NotSaved'); + $collection->drop(); + $test = array( + 'name' => 'Jonathan Wage', + 'notSaved' => 'test' + ); + $collection->insert($test); + $notSaved = $this->dm->find('Documents\Functional\NotSaved', $test['_id']); + $this->assertEquals('Jonathan Wage', $notSaved->name); + $this->assertEquals('test', $notSaved->notSaved); + + $notSaved = new NotSaved(); + $notSaved->name = 'Roman Borschel'; + $notSaved->notSaved = 'test'; + $this->dm->persist($notSaved); + $this->dm->flush(); + $this->dm->clear(); + + $notSaved = $collection->findOne(array('name' => 'Roman Borschel')); + $this->assertEquals('Roman Borschel', $notSaved['name']); + $this->assertFalse(isset($notSaved['notSaved'])); + } + + public function testFavoritesReference() + { + $project = new Project('Test Project'); + $this->dm->persist($project); + $this->dm->flush(); + + $group = new Group('Test Group'); + $this->dm->persist($group); + $this->dm->flush(); + + $user = new FavoritesUser(); + $user->setName('favorites'); + $user->addFavorite($project); + $user->addFavorite($group); + + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + $address->setCity('Nashville'); + $address->setState('TN'); + $address->setZipcode('37209'); + + $user->embed($address); + $user->setEmbed($address); + + $document = new Phonenumber('6155139185'); + $user->embed($document); + $user->setFavorite($project); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection('Documents\Functional\FavoritesUser')->findOne(array('name' => 'favorites')); + $this->assertTrue(isset($test['favorites'][0]['type'])); + $this->assertEquals('project', $test['favorites'][0]['type']); + $this->assertEquals('group', $test['favorites'][1]['type']); + $this->assertTrue(isset($test['favorite']['_doctrine_class_name'])); + $this->assertEquals('Documents\Project', $test['favorite']['_doctrine_class_name']); + + $user = $this->dm->getRepository('Documents\Functional\FavoritesUser')->findOneBy(array('name' => 'favorites')); + $favorites = $user->getFavorites(); + $this->assertInstanceOf('Documents\Project', $favorites[0]); + $this->assertInstanceOf('Documents\Group', $favorites[1]); + + $embedded = $user->getEmbedded(); + $this->assertInstanceOf('Documents\Address', $embedded[0]); + $this->assertInstanceOf('Documents\Phonenumber', $embedded[1]); + + $this->assertInstanceOf('Documents\Address', $user->getEmbed()); + $this->assertInstanceOf('Documents\Project', $user->getFavorite()); + } + + public function testPreUpdate() + { + $product = new PreUpdateTestProduct(); + $product->name = 'Product'; + + $seller = new PreUpdateTestSeller(); + $seller->name = 'Seller'; + + $this->dm->persist($seller); + $this->dm->persist($product); + $this->dm->flush(); + + $sellable = new PreUpdateTestSellable(); + $sellable->product = $product; + $sellable->seller = $seller; + + $product->sellable = $sellable; + + $this->dm->flush(); + $this->dm->clear(); + + $product = $this->dm->getRepository('Documents\Functional\PreUpdateTestProduct')->findOneBy(array('name' => 'Product')); + + $this->assertInstanceOf('Documents\Functional\PreUpdateTestSellable', $product->sellable); + $this->assertInstanceOf('Documents\Functional\PreUpdateTestProduct', $product->sellable->getProduct()); + $this->assertInstanceOf('Documents\Functional\PreUpdateTestSeller', $product->sellable->getSeller()); + + $product = new PreUpdateTestProduct(); + $product->name = 'Product2'; + + $this->dm->persist($product); + $this->dm->flush(); + + $sellable = new PreUpdateTestSellable(); + $sellable->product = $product; + $sellable->seller = $this->dm->getRepository('Documents\Functional\PreUpdateTestSeller')->findOneBy(array('name' => 'Seller')); + + $product->sellable = $sellable; + + $this->dm->flush(); + $this->dm->clear(); + + $product = $this->dm->getRepository('Documents\Functional\PreUpdateTestProduct')->findOneBy(array('name' => 'Product2')); + $this->assertEquals('Seller', $product->sellable->getSeller()->getName()); + $this->assertEquals('Product2', $product->sellable->getProduct()->getName()); + } + + public function testSameCollectionTest() + { + $test1 = new SameCollection1(); + $test1->name = 'test1'; + $this->dm->persist($test1); + + $test2 = new SameCollection2(); + $test2->name = 'test2'; + $this->dm->persist($test2); + $this->dm->flush(); + + $test = $this->dm->getRepository('Documents\Functional\SameCollection1')->findOneBy(array('name' => 'test1')); + $this->assertNotNull($test); + $this->assertInstanceOf('Documents\Functional\SameCollection1', $test); + + $test = $this->dm->getRepository('Documents\Functional\SameCollection2')->findOneBy(array('name' => 'test2')); + $this->assertNotNull($test); + $this->assertInstanceOf('Documents\Functional\SameCollection2', $test); + + $test = $this->dm->getRepository('Documents\Functional\SameCollection2')->findOneBy(array('name' => 'test1')); + $this->assertNull($test); + + $qb = $this->dm->createQueryBuilder(array( + 'Documents\Functional\SameCollection1', + 'Documents\Functional\SameCollection2') + ); + $q = $qb->getQuery(); + $test = $q->execute(); + $this->assertEquals(2, count($test)); + + $test = $this->dm->getRepository('Documents\Functional\SameCollection1')->findAll(); + $this->assertEquals(1, count($test)); + + $qb = $this->dm->createQueryBuilder('Documents\Functional\SameCollection1'); + $query = $qb->getQuery(); + $test = $query->execute(); + $this->assertEquals(1, count($test)); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testNotSameCollectionThrowsException() + { + $test = $this->dm->createQueryBuilder(array( + 'Documents\User', + 'Documents\Profile') + )->getQuery()->execute(); + } + + public function testEmbeddedNesting() + { + $test = new EmbeddedTestLevel0(); + $test->name = 'test'; + + $level1_0 = new EmbeddedTestLevel1(); + $level1_0->name = 'test level1 #1'; + $test->level1[0] = $level1_0; + + $level1_1 = new EmbeddedTestLevel1(); + $level1_1->name = 'test level1 #2'; + $test->level1[1] = $level1_1; + + $level2_0 = new EmbeddedTestLevel2(); + $level2_0->name = 'test level2 #1'; + $level1_1->level2[0] = $level2_0; + + $level2_1 = new EmbeddedTestLevel2(); + $level2_1->name = 'test level2 #2'; + $level1_1->level2[1] = $level2_1; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getRepository('Documents\Functional\EmbeddedTestLevel0')->find($test->id); + $this->assertEquals('test', $check->name); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel1', $check->level1[0]); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel1', $check->level1[1]); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel2', $check->level1[1]->level2[0]); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel2', $check->level1[1]->level2[1]); + $this->assertEquals(2, count($check->level1)); + $this->assertEquals(2, count($check->level1[1]->level2)); + } + + public function testEmbeddedInheritance() + { + // create a level0b (inherits from level0) + $test = new EmbeddedTestLevel0b(); + $test->name = 'test b'; + + // embed a level1 + $level1 = new EmbeddedTestLevel1(); + $level1->name = 'level 1'; + $test->oneLevel1 = $level1; + + // save the level0b + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + // fetch the level0b from db + $test = $this->dm->find('Documents\Functional\EmbeddedTestLevel0b', $test->id); + + // add a level2 in the level0b.level1 + $level2 = new EmbeddedTestLevel2(); + $level2->name = 'level 2'; + $test->oneLevel1->level2[] = $level2; + + // OK, there is one level2 + $this->assertEquals(1, count($test->oneLevel1->level2)); + + // save again + $this->dm->flush(); + $this->dm->clear(); + + // fetch again + $test = $this->dm->find('Documents\Functional\EmbeddedTestLevel0b', $test->id); + + // Uh oh, the level2 was not persisted! + $this->assertEquals(1, count($test->oneLevel1->level2)); + } + + public function testModifyGroupsArrayDirectly() + { + $account = new Account(); + $account->setName('Jon Test Account'); + + $user = new User(); + $user->setUsername('jon333'); + $user->setPassword('changeme'); + $user->setAccount($account); + + $user->addGroup(new Group('administrator')); + $user->addGroup(new Group('member')); + $user->addGroup(new Group('moderator')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertNotNull($user); + + // remove two of the groups and pass the groups back into the User + $groups = $user->getGroups(); + unset($groups[0]); + unset($groups[2]); + + $user->setGroups($groups); + + $this->assertEquals(1, count($user->getGroups())); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertEquals(1, count($user->getGroups())); + } + + public function testReplaceEntireGroupsArray() + { + $account = new Account(); + $account->setName('Jon Test Account'); + + $user = new User(); + $user->setUsername('jon333'); + $user->setPassword('changeme'); + $user->setAccount($account); + + $group2 = new Group('member'); + $user->addGroup(new Group('administrator')); + $user->addGroup($group2); + $user->addGroup(new Group('moderator')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertNotNull($user); + + // Issue is collection must be initialized + $groups = $user->getGroups(); + $groups[0]; // initialize collection + + // reffectively remove two of the groups + //$user->getGroups()->clear(); + //$user->getGroups()->add($group2); + + $user->setGroups(array($group2)); + + $this->assertEquals(1, count($user->getGroups())); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertEquals(1, count($user->getGroups())); + } + + public function testFunctionalParentAssociations() + { + $a = new ParentAssociationTestA('a'); + $a->child = new ParentAssociationTestB('b'); + $a->child->children[] = new ParentAssociationTestC('c1'); + $a->child->children[] = new ParentAssociationTestC('c2'); + $this->dm->persist($a); + $this->dm->flush(); + + $unitOfWork = $this->dm->getUnitOfWork(); + + list($mapping, $document) = $unitOfWork->getParentAssociation($a->child->children[0]); + $this->assertSame($a->child, $document); + + list($mapping, $document) = $unitOfWork->getParentAssociation($a->child->children[1]); + $this->assertSame($a->child, $document); + + list($mapping, $document) = $unitOfWork->getParentAssociation($a->child); + $this->assertSame($a, $document); + } +} + +/** @ODM\Document */ +class ParentAssociationTestA +{ + /** @ODM\Id */ + public $id; + /** @ODM\String */ + public $name; + /** @ODM\EmbedOne */ + public $child; + public function __construct($name) + { + $this->name = $name; + } +} + +/** @ODM\EmbeddedDocument */ +class ParentAssociationTestB +{ + /** @ODM\String */ + public $name; + /** @ODM\EmbedMany */ + public $children = array(); + public function __construct($name) + { + $this->name = $name; + } +} + +/** @ODM\EmbeddedDocument */ +class ParentAssociationTestC +{ + /** @ODM\String */ + public $name; + public function __construct($name) + { + $this->name = $name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/GeoSpatialTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/GeoSpatialTest.php new file mode 100644 index 00000000..be205791 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/GeoSpatialTest.php @@ -0,0 +1,201 @@ +dm->createQueryBuilder(__NAMESPACE__.'\City') + ->geoNear(1000000, 11111); + $this->assertEquals(array('near' => array(1000000, 11111)), $qb->debug('geoNear')); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->withinBox(41, 41, 72, 72); + $this->assertEquals(array( + 'coordinates' => array( + '$within' => array('$box' => array(array(41, 41), array(72, 72))) + ) + ), $qb->getQueryArray()); + } + + public function testGetFieldsInCoordinatesQuery() + { + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\City'); + $qb->field('coordinates')->withinBox(41, 41, 72, 72); + $query = $qb->getQuery(); + $this->assertEquals(array('coordinates'), $query->getFieldsInQuery()); + } + + public function testGeoSpatial1() + { + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__.'\City'); + + $city = new City(); + $city->name = 'Nashville'; + $city->coordinates = new Coordinates(); + $city->coordinates->latitude = 50; + $city->coordinates->longitude = 30; + + $this->dm->persist($city); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->geoNear(1000000, 11111); + $query = $qb->getQuery(); + $city = $query->getSingleResult(); + $this->assertNull($city); + + $city = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->geoNear(50, 50) + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($city); + + $this->assertEquals(20, round($city->test)); + + $query = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->near(50, 50) + ->getQuery(); + foreach ($query as $city2) { + $this->assertEquals($city, $city2); + } + } + + public function testGeoSpatial2() + { + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__.'\City'); + + $city = new City(); + $city->name = 'Nashville'; + $city->coordinates = new Coordinates(); + $city->coordinates->latitude = 34.2055968; + $city->coordinates->longitude = -118.8713314; + + $this->dm->persist($city); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $city = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->near(50, 50) + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($city); + } + + public function testWithinBox() + { + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__.'\City'); + + $city = new City(); + $city->name = 'Nashville'; + $city->coordinates = new Coordinates(); + $city->coordinates->latitude = 40.739037; + $city->coordinates->longitude = 73.992964; + + $this->dm->persist($city); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $city = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->withinBox(41, 41, 72, 72) + ->getQuery() + ->getSingleResult(); + $this->assertNull($city); + + $city = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->withinBox(30, 30, 80, 80) + ->field('name')->equals('Nashville') + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($city); + } + + public function testWithinCenter() + { + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__.'\City'); + + $city = new City(); + $city->name = 'Nashville'; + $city->coordinates = new Coordinates(); + $city->coordinates->latitude = 50; + $city->coordinates->longitude = 30; + + $this->dm->persist($city); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $city = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->withinCenter(50, 50, 20) + ->field('name')->equals('Nashville') + ->getQuery() + ->getSingleResult(); + $this->assertNotNull($city); + } + + public function testGeoNearDistanceIsNotNullForFilteredQuery() + { + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__.'\City'); + + $city1 = new City(); + $city1->name = 'Nashville'; + $city1->coordinates = new Coordinates(); + $city1->coordinates->latitude = 30; + $city1->coordinates->longitude = 40; + + $city2 = new City(); + $city2->name = 'Columbus'; + $city2->coordinates = new Coordinates(); + $city2->coordinates->latitude = 40; + $city2->coordinates->longitude = 30; + + $this->dm->persist($city1); + $this->dm->persist($city2); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $query = $this->dm->createQueryBuilder(__NAMESPACE__.'\City') + ->field('coordinates')->geoNear(35, 35) + ->field('id')->in(array($city1->id)) + ->getQuery(); + + foreach ($query as $city) { + $this->assertEquals($city->name, $city1->name); + $this->assertNotEquals($city->name, $city2->name); + $this->assertNotNull($city->test); + } + } + +} + +/** + * @ODM\Document + * @ODM\Index(keys={"coordinates"="2d"}) + */ +class City +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedOne(targetDocument="Coordinates") */ + public $coordinates; + + /** @ODM\Distance */ + public $test; +} + +/** @ODM\EmbeddedDocument */ +class Coordinates +{ + /** @ODM\Float */ + public $latitude; + + /** @ODM\Float */ + public $longitude; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php new file mode 100644 index 00000000..be67127c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php @@ -0,0 +1,220 @@ +dm->persist($user); + $this->dm->flush(); + $id = $user->id; + + $this->dm->clear(); + $check1 = $this->dm->getRepository(__NAMESPACE__.'\UuidUser')->findOneBy(array('id' => $id)); + $this->assertNotNull($check1); + + $check2 = $this->dm->createQueryBuilder(__NAMESPACE__.'\UuidUser') + ->field('id')->equals($id)->getQuery()->getSingleResult(); + $this->assertNotNull($check2); + $this->assertSame($check1, $check2); + + $check3 = $this->dm->createQueryBuilder(__NAMESPACE__.'\UuidUser') + ->field('name')->equals('Jonathan H. Wage')->getQuery()->getSingleResult(); + $this->assertNotNull($check3); + $this->assertSame($check2, $check3); + } + + public function testAlnumIdChars() + { + $user = new AlnumCharsUser('Jonathan H. Wage'); + $this->dm->persist($user); + $user = new AlnumCharsUser('Kathrine R. Cage'); + $this->dm->persist($user); + $this->dm->flush(); + + $this->dm->clear(); + $check1 = $this->dm->getRepository(__NAMESPACE__.'\AlnumCharsUser')->findOneBy(array('id' => 'x')); + $this->assertNotNull($check1); + + $check2 = $this->dm->createQueryBuilder(__NAMESPACE__.'\AlnumCharsUser') + ->field('id')->equals('x')->getQuery()->getSingleResult(); + $this->assertNotNull($check2); + $this->assertSame($check1, $check2); + + $check3 = $this->dm->createQueryBuilder(__NAMESPACE__.'\AlnumCharsUser') + ->field('name')->equals('Kathrine R. Cage')->getQuery()->getSingleResult(); + $this->assertNotNull($check3); + $this->assertSame($check2, $check3); + } + + public function testCollectionId() + { + $user1 = new CollectionIdUser('Jonathan H. Wage'); + $reference1 = new ReferencedCollectionId('referenced 1'); + $user1->reference = $reference1; + + $user2 = new CollectionIdUser('Jonathan H. Wage'); + + $reference2 = new ReferencedCollectionId('referenced 2'); + $user2->reference = $reference2; + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertEquals($user1->id, 1); + $this->assertEquals($user2->id, 2); + + $this->assertEquals($reference1->id, 1); + $this->assertEquals($reference2->id, 2); + + $check1 = $this->dm->getRepository(__NAMESPACE__.'\CollectionIdUser')->findOneBy(array('id' => $user1->id)); + $check2 = $this->dm->getRepository(__NAMESPACE__.'\CollectionIdUser')->findOneBy(array('id' => $user2->id)); + $this->assertNotNull($check1); + $this->assertNotNull($check2); + + $this->assertEquals('referenced 1', $check1->reference->getName()); + $this->assertEquals('referenced 2', $check2->reference->getName()); + + $check = $this->dm->getRepository(__NAMESPACE__.'\CollectionIdUser')->find((string) $user1->id); + $this->assertNotNull($check); + + } + + public function testEmbeddedDocumentWithId() + { + $user1 = new CollectionIdUser('Jonathan H. Wage'); + $user1->embedded[] = new EmbeddedCollectionId('embedded #1'); + $user1->embedded[] = new EmbeddedCollectionId('embedded #2'); + $this->dm->persist($user1); + $this->dm->flush(); + + $user2 = new CollectionIdUser('Jonathan H. Wage'); + $user2->embedded[] = new EmbeddedCollectionId('embedded #1'); + $user2->embedded[] = new EmbeddedCollectionId('embedded #2'); + $this->dm->persist($user2); + $this->dm->flush(); + + $this->assertEquals($user1->id, 1); + $this->assertEquals($user2->id, 2); + + $this->assertEquals($user1->embedded[0]->id, 1); + $this->assertEquals($user1->embedded[1]->id, 2); + + $this->assertEquals($user2->embedded[0]->id, 3); + $this->assertEquals($user2->embedded[1]->id, 4); + } + + public function testIdGeneratorInstance() + { + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\UuidUser'); + $this->assertEquals(\Doctrine\ODM\MongoDB\Mapping\ClassMetadata::GENERATOR_TYPE_UUID, $class->generatorType); + $this->assertEquals(array('salt' => 'test'), $class->generatorOptions); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Id\UuidGenerator', $class->idGenerator); + $this->assertEquals('test', $class->idGenerator->getSalt()); + + $serialized = serialize($class); + $class = unserialize($serialized); + + $this->assertEquals(\Doctrine\ODM\MongoDB\Mapping\ClassMetadata::GENERATOR_TYPE_UUID, $class->generatorType); + $this->assertEquals(array('salt' => 'test'), $class->generatorOptions); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Id\UuidGenerator', $class->idGenerator); + $this->assertEquals('test', $class->idGenerator->getSalt()); + } +} + +/** @ODM\Document */ +class UuidUser +{ + /** @ODM\Id(strategy="uuid", options={"salt"="test"}) */ + public $id; + + /** @ODM\String(name="t") */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} + +/** @ODM\Document */ +class CollectionIdUser +{ + /** @ODM\Id(strategy="increment") */ + public $id; + + /** @ODM\String(name="t") */ + public $name; + + /** @ODM\ReferenceOne(targetDocument="ReferencedCollectionId", cascade={"persist"}) */ + public $reference; + + /** @ODM\EmbedMany(targetDocument="EmbeddedCollectionId") */ + public $embedded = array(); + + public function __construct($name) + { + $this->name = $name; + } +} + +/** @ODM\Document */ +class ReferencedCollectionId +{ + /** @ODM\Id(strategy="increment") */ + public $id; + + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} + +/** @ODM\EmbeddedDocument */ +class EmbeddedCollectionId +{ + /** @ODM\Id(strategy="increment") */ + public $id; + + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} + +/** @ODM\Document */ +class AlnumCharsUser +{ + /** @ODM\Id(strategy="alnum", options={"chars"="zyxwvutsrqponmlkjihgfedcba"}) */ + public $id; + + /** @ODM\String(name="t") */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php new file mode 100644 index 00000000..1a4818c9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -0,0 +1,101 @@ +setUsername('jwage'); + $event = new \Documents\Event(); + $event->setTitle('test event title'); + $event->setUser($user); + $this->dm->persist($user); + $this->dm->persist($event); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository(get_class($event))->find($event->getId()); + + $this->assertEquals($user->getId(), $test->getUser()->getId()); + $this->assertFalse($test->getUser()->__isInitialized__); + + $this->dm->clear(); + + $class = $this->dm->getClassMetadata(get_class($test->getUser())); + + $test = $this->dm->getRepository(get_class($event))->find($event->getId()); + $this->assertEquals($user->getId(), $class->getIdentifierValue($test->getUser())); + $this->assertEquals($user->getId(), $class->getFieldValue($test->getUser(), 'id')); + $this->assertFalse($test->getUser()->__isInitialized__); + + $this->assertEquals('jwage', $test->getUser()->getUsername()); + $this->assertTrue($test->getUser()->__isInitialized__); + } + + public function testIdentifiersAreSet() + { + $user = new \Documents\User(); + $user->setUsername('jwage'); + $user->setPassword('test'); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->assertTrue($user->getId() !== ''); + } + + public function testIdentityMap() + { + $user = new \Documents\User(); + $user->setUsername('jwage'); + + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + + $user = $qb->getQuery()->getSingleResult(); + $this->assertSame($user, $user); + + $this->dm->clear(); + + $user2 = $qb->getQuery()->getSingleResult(); + $this->assertNotSame($user, $user2); + + $user2->setUsername('changed'); + + $qb->refresh(); + + $user3 = $qb->getQuery()->getSingleResult(); + $this->assertSame($user2, $user3); + $this->assertEquals('jwage', $user3->getUsername()); + + $user3->setUsername('changed'); + + $qb->refresh(false); + + $user4 = $qb->getQuery()->getSingleResult(); + $this->assertEquals('changed', $user4->getUsername()); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->findAndUpdate() + ->returnNew(true) + ->hydrate(true) + ->field('username')->equals('jwage') + ->field('count')->inc(1); + + $result = $qb->refresh(false)->getQuery()->execute(); + $this->assertEquals(0, $result->getCount()); + + $result = $qb->refresh(false)->getQuery()->execute(); + $this->assertEquals(0, $result->getCount()); + + $result = $qb->refresh(true)->getQuery()->execute(); + $this->assertEquals(3, $result->getCount()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php new file mode 100644 index 00000000..251b3190 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php @@ -0,0 +1,362 @@ +dm->getSchemaManager()->ensureDocumentIndexes($class); + + $test = new $class(); + $test->username = 'jwage'; + $test->email = 'jonwage@gmail.com'; + $this->dm->persist($test); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $test = new $class(); + $test->username = 'jwage'; + $test->email = 'jonathan.wage@sensio.com'; + $this->dm->persist($test); + $this->dm->flush(null, array('safe' => true)); + + $test = new $class(); + $test->username = 'jwage'; + $test->email = 'jonathan.wage@sensio.com'; + $this->dm->persist($test); + $this->dm->flush(null, array('safe' => true)); + } + + public function testEmbeddedIndexes() + { + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\DocumentWithEmbeddedIndexes'); + $sm = $this->dm->getSchemaManager(); + $indexes = $sm->getDocumentIndexes($class->name); + + $this->assertTrue(isset($indexes[0]['keys']['embedded.name'])); + $this->assertEquals(1, $indexes[0]['keys']['embedded.name']); + + $this->assertTrue(isset($indexes[1]['keys']['embedded.embeddedMany.name'])); + $this->assertEquals(1, $indexes[1]['keys']['embedded.embeddedMany.name']); + } + + public function testDiscriminatorIndexes() + { + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\DocumentWithDiscriminatorIndex'); + $sm = $this->dm->getSchemaManager(); + $indexes = $sm->getDocumentIndexes($class->name); + + $this->assertTrue(isset($indexes[0]['keys']['type'])); + $this->assertEquals(1, $indexes[0]['keys']['type']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\DocumentWithRenamedDiscriminatorIndex'); + $sm = $this->dm->getSchemaManager(); + $indexes = $sm->getDocumentIndexes($class->name); + + $this->assertTrue(isset($indexes[0]['keys']['typeMongo'])); + $this->assertEquals(1, $indexes[0]['keys']['typeMongo']); + } + + public function testIndexDefinitions() + { + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\UniqueOnFieldTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + $this->assertEquals(true, $indexes[0]['options']['safe']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\UniqueOnDocumentTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\IndexesOnDocumentTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\UniqueSparseOnFieldTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + $this->assertEquals(true, $indexes[0]['options']['safe']); + $this->assertTrue(isset($indexes[0]['options']['sparse'])); + $this->assertEquals(true, $indexes[0]['options']['sparse']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\UniqueSparseOnDocumentTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + $this->assertTrue(isset($indexes[0]['options']['sparse'])); + $this->assertEquals(true, $indexes[0]['options']['sparse']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\SparseIndexesOnDocumentTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + $this->assertTrue(isset($indexes[0]['options']['sparse'])); + $this->assertEquals(true, $indexes[0]['options']['sparse']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\MultipleFieldsUniqueIndexTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['keys']['email'])); + $this->assertEquals(1, $indexes[0]['keys']['email']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\MultipleFieldsUniqueSparseIndexTest'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['keys']['email'])); + $this->assertEquals(1, $indexes[0]['keys']['email']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + $this->assertTrue(isset($indexes[0]['options']['sparse'])); + $this->assertEquals(true, $indexes[0]['options']['sparse']); + + $class = $this->dm->getClassMetadata(__NAMESPACE__.'\MultipleFieldIndexes'); + $indexes = $class->getIndexes(); + $this->assertTrue(isset($indexes[0]['keys']['username'])); + $this->assertEquals(1, $indexes[0]['keys']['username']); + $this->assertTrue(isset($indexes[0]['options']['unique'])); + $this->assertEquals(true, $indexes[0]['options']['unique']); + + $this->assertTrue(isset($indexes[1]['keys']['email'])); + $this->assertEquals(1, $indexes[1]['keys']['email']); + $this->assertTrue(isset($indexes[1]['options']['unique'])); + $this->assertEquals(true, $indexes[1]['options']['unique']); + $this->assertEquals('test', $indexes[0]['options']['name']); + } + + /** + * @expectedException MongoCursorException + */ + public function testUniqueIndexOnField() + { + $this->uniqueTest('UniqueOnFieldTest'); + } + + /** + * @expectedException MongoCursorException + */ + public function testUniqueIndexOnDocument() + { + $this->uniqueTest('UniqueOnDocumentTest'); + } + + /** + * @expectedException MongoCursorException + */ + public function testIndexesOnDocument() + { + $this->uniqueTest('IndexesOnDocumentTest'); + } + + /** + * @expectedException MongoCursorException + */ + public function testMultipleFieldsUniqueIndexOnDocument() + { + $this->uniqueTest('MultipleFieldsUniqueIndexTest'); + } + + /** + * @expectedException MongoCursorException + */ + public function testMultipleFieldIndexes() + { + $this->uniqueTest('MultipleFieldIndexes'); + } +} + +/** @ODM\Document */ +class UniqueOnFieldTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String @ODM\UniqueIndex(safe=true) */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\UniqueIndex(keys={"username"="asc"}) */ +class UniqueOnDocumentTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\Indexes(@ODM\UniqueIndex(keys={"username"="asc"})) */ +class IndexesOnDocumentTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\UniqueIndex(keys={"username"="asc", "email"="asc"}) */ +class MultipleFieldsUniqueIndexTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document */ +class UniqueSparseOnFieldTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String @ODM\UniqueIndex(safe=true, sparse=true) */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\UniqueIndex(keys={"username"="asc"}, options={"sparse"=true}) */ +class UniqueSparseOnDocumentTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\Indexes(@ODM\UniqueIndex(keys={"username"="asc"}, options={"sparse"=true})) */ +class SparseIndexesOnDocumentTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document @ODM\UniqueIndex(keys={"username"="asc", "email"="asc"}, options={"sparse"=true}) */ +class MultipleFieldsUniqueSparseIndexTest +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $username; + + /** @ODM\String */ + public $email; +} + +/** @ODM\Document */ +class MultipleFieldIndexes +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String @ODM\UniqueIndex(name="test") */ + public $username; + + /** @ODM\String @ODM\Index(unique=true) */ + public $email; +} + +/** @ODM\Document */ +class DocumentWithEmbeddedIndexes +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedOne(targetDocument="EmbeddedDocumentWithIndexes") */ + public $embedded; +} + +/** + * @ODM\Document + * @ODM\DiscriminatorField(fieldName="type") + */ +class DocumentWithDiscriminatorIndex +{ + /** @ODM\Id */ + public $id; + + /** ODM\String @ODM\Index */ + public $type; +} + +/** + * @ODM\Document + * @ODM\DiscriminatorField(fieldName="typeMongo",name="typePhp") + */ +class DocumentWithRenamedDiscriminatorIndex +{ + /** @ODM\Id */ + public $id; + + /** ODM\String @ODM\Index */ + public $typePhp; +} + +/** @ODM\EmbeddedDocument */ +class EmbeddedDocumentWithIndexes +{ + /** @ODM\String @ODM\Index */ + public $name; + + /** @ODM\EmbedMany(targetDocument="EmbeddedManyDocumentWithIndexes") */ + public $embeddedMany; +} + +/** @ODM\EmbeddedDocument */ +class EmbeddedManyDocumentWithIndexes +{ + /** @ODM\String @ODM\Index */ + public $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/InheritanceTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/InheritanceTest.php new file mode 100644 index 00000000..bba306cf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/InheritanceTest.php @@ -0,0 +1,109 @@ +setFirstName('Jon'); + + $user = new \Documents\SpecialUser(); + $user->setUsername('specialuser'); + $user->setProfile($profile); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertTrue($user->getId() !== ''); + $this->assertTrue($user->getProfile()->getProfileId() !== ''); + + $qb = $this->dm->createQueryBuilder('Documents\SpecialUser') + ->field('id') + ->equals($user->getId()); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + + $user->getProfile()->setLastName('Wage'); + $this->dm->flush(); + $this->dm->clear(); + + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertEquals('Wage', $user->getProfile()->getLastName()); + $this->assertTrue($user instanceof \Documents\SpecialUser); + } + + public function testSingleCollectionInhertiance() + { + $subProject = new \Documents\SubProject('Sub Project'); + $this->dm->persist($subProject); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection('Documents\SubProject'); + $document = $coll->findOne(array('name' => 'Sub Project')); + $this->assertEquals('sub-project', $document['type']); + + $project = new \Documents\OtherSubProject('Other Sub Project'); + $this->dm->persist($project); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection('Documents\OtherSubProject'); + $document = $coll->findOne(array('name' => 'Other Sub Project')); + $this->assertEquals('other-sub-project', $document['type']); + + $this->dm->clear(); + + $document = $this->dm->getRepository('Documents\SubProject')->findOneBy(array('name' => 'Sub Project')); + $this->assertInstanceOf('Documents\SubProject', $document); + + $document = $this->dm->getRepository('Documents\SubProject')->findOneBy(array('name' => 'Sub Project')); + $this->assertInstanceOf('Documents\SubProject', $document); + + $document = $this->dm->getRepository('Documents\Project')->findOneBy(array('name' => 'Sub Project')); + $this->assertInstanceOf('Documents\SubProject', $document); + $this->dm->clear(); + + $id = $document->getId(); + $document = $this->dm->find('Documents\Project', $id); + $this->assertInstanceOf('Documents\SubProject', $document); + + $document = $this->dm->getRepository('Documents\Project')->findOneBy(array('name' => 'Other Sub Project')); + $this->assertInstanceOf('Documents\OtherSubProject', $document); + } + + public function testPrePersistIsCalledFromMappedSuperClass() + { + $user = new \Documents\User(); + $user->setUsername('test'); + $this->dm->persist($user); + $this->dm->flush(); + $this->assertTrue($user->persisted); + } + + public function testInheritanceProxy() + { + $developer = new \Documents\Developer('avalanche123'); + + $projects = $developer->getProjects(); + + $projects->add(new \Documents\Project('Main Project')); + $projects->add(new \Documents\SubProject('Sub Project')); + $projects->add(new \Documents\OtherSubProject('Another Sub Project')); + + $this->dm->persist($developer); + $this->dm->flush(); + $this->dm->clear(); + + $developer = $this->dm->find('Documents\Developer', $developer->getId()); + $projects = $developer->getProjects(); + + $this->assertEquals(3, $projects->count()); + + $this->assertInstanceOf('Documents\Project', $projects[0]); + $this->assertInstanceOf('Documents\SubProject', $projects[1]); + $this->assertInstanceOf('Documents\OtherSubProject', $projects[2]); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php new file mode 100644 index 00000000..75dfb718 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php @@ -0,0 +1,159 @@ +dm->persist($parent); + $this->dm->flush(); + + $this->assertEquals(1, count($parent->getChildren())); + + $parent->setName('parent #changed'); + + $this->dm->flush(); + $this->dm->flush(); + + $this->assertEquals(1, count($parent->getChildren())); + + $this->dm->clear(); + + $parent = $this->dm->getRepository(__NAMESPACE__.'\ParentObject')->find($parent->getId()); + $this->assertNotNull($parent); + $this->assertEquals('parent #changed', $parent->getName()); + $this->assertEquals(1, count($parent->getChildren())); + $this->assertEquals('changed', $parent->getChildEmbedded()->getName()); + } + + public function testEventEmptyFlush() + { + $parent = new ParentObject('parent', new ChildObject('child'), new ChildEmbeddedObject('child embedded')); + + $this->dm->persist($parent); + $this->dm->flush(); + + $parent->setName('parent #changed'); + + $this->dm->flush(); + $this->dm->clear(); + + $parent = $this->dm->getRepository(__NAMESPACE__.'\ParentObject')->find($parent->getId()); + $this->assertNotNull($parent); + $this->assertEquals(1, count($parent->getChildren())); + } +} + +/** @ODM\Document */ +class ParentObject +{ + /** @ODM\Id */ + private $id; + + /** @ODM\ReferenceMany(targetDocument="ChildObject", cascade="all") */ + private $children; + + /** @ODM\String */ + private $name; + + /** @ODM\EmbedOne(targetDocument="ChildEmbeddedObject") */ + private $childEmbedded; + + private $child; + + public function __construct($name, ChildObject $child, ChildEmbeddedObject $childEmbedded) + { + $this->name = $name; + $this->child = $child; + $this->childEmbedded = $childEmbedded; + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + /** @ODM\PrePersist @ODM\PreUpdate */ + public function prePersistPreUpdate() + { + $this->children = array($this->child); + } + + /** @ODM\PreUpdate */ + public function preUpdate() + { + $this->childEmbedded->setName('changed'); + } + + public function getChildren() + { + return $this->children; + } + + public function getChildEmbedded() + { + return $this->childEmbedded; + } + + public function setName($name) + { + $this->name = $name; + } +} + +/** @ODM\Document */ +class ChildObject +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } +} + +/** @ODM\EmbeddedDocument */ +class ChildEmbeddedObject +{ + /** @ODM\String */ + private $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php new file mode 100644 index 00000000..563cace0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php @@ -0,0 +1,390 @@ +dm->persist($article); + $this->dm->flush(); + + $this->assertEquals(1, $article->version); + + $article->title = 'test'; + $this->dm->flush(); + + $this->assertEquals(2, $article->version); + } + + public function testOptimisticLockingIntThrowsException() + { + $article = new LockInt('Test LockInt'); + $this->dm->persist($article); + $this->dm->flush(); + + // Manually change the version so the next code will cause an exception + $this->dm->getDocumentCollection(get_class($article))->update(array('_id' => new \MongoId($article->id)), array('$set' => array('version' => 5))); + + // Now lets change a property and try and save it again + $article->title = 'ok'; + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->flush(); + } + + public function testMultipleFlushesDoIncrementalUpdates() + { + $test = new LockInt(); + + for ($i = 0; $i < 5; $i++) { + $test->title = 'test' . $i; + $this->dm->persist($test); + $this->dm->flush(); + + $this->assertInternalType('int', $test->getVersion()); + $this->assertEquals($i + 1, $test->getVersion()); + } + } + + public function testLockTimestampSetsDefaultValue() + { + $test = new LockTimestamp(); + $test->title = 'Testing'; + + $this->assertNull($test->version, "Pre-Condition"); + + $this->dm->persist($test); + $this->dm->flush(); + + $date1 = $test->version; + + $this->assertInstanceOf('DateTime', $date1); + + $test->title = 'changed'; + $this->dm->flush(); + + $this->assertNotSame($date1, $test->version); + + return $test; + } + + public function testLockTimestampThrowsException() + { + $article = new LockTimestamp('Test LockInt'); + $this->dm->persist($article); + $this->dm->flush(); + + // Manually change the version so the next code will cause an exception + $this->dm->getDocumentCollection(get_class($article))->update(array('_id' => new \MongoId($article->id)), array('$set' => array('version' => new \MongoDate(time() + 600)))); + + // Now lets change a property and try and save it again + $article->title = 'ok'; + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->flush(); + } + + public function testLockVersionedDocument() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::OPTIMISTIC, $article->version); + } + + public function testLockVersionedDocumentMissmatchThrowsException() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->lock($article, LockMode::OPTIMISTIC, $article->version + 1); + } + + public function testLockUnversionedDocumentThrowsException() + { + $user = new \Documents\User(); + $user->setUsername('test'); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException', 'Document Documents\User is not versioned.'); + + $this->dm->lock($user, LockMode::OPTIMISTIC); + } + + public function testLockUnmanagedDocumentThrowsException() + { + $article = new LockInt(); + + $this->setExpectedException('InvalidArgumentException', 'Document is not MANAGED.'); + + $this->dm->lock($article, LockMode::OPTIMISTIC, $article->version + 1); + } + + public function testLockPessimisticWrite() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::PESSIMISTIC_WRITE); + + $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $this->assertEquals(LockMode::PESSIMISTIC_WRITE, $check['locked']); + } + + public function testLockPessimisticRead() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::PESSIMISTIC_READ); + + $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $this->assertEquals(LockMode::PESSIMISTIC_READ, $check['locked']); + } + + public function testUnlock() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::PESSIMISTIC_READ); + + $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $this->assertEquals(LockMode::PESSIMISTIC_READ, $check['locked']); + $this->assertEquals(LockMode::PESSIMISTIC_READ, $article->locked); + + $this->dm->unlock($article); + + $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $this->assertFalse(isset($check['locked'])); + $this->assertNull($article->locked); + } + + public function testPessimisticReadLockThrowsExceptionOnRemove() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt'); + $coll->update(array('_id' => new \MongoId($article->id)), array('locked' => LockMode::PESSIMISTIC_READ)); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->remove($article); + $this->dm->flush(); + } + + public function testPessimisticReadLockThrowsExceptionOnUpdate() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt'); + $coll->update(array('_id' => new \MongoId($article->id)), array('locked' => LockMode::PESSIMISTIC_READ)); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $article->title = 'changed'; + $this->dm->flush(); + } + + public function testPessimisticWriteLockThrowExceptionOnRemove() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt'); + $coll->update(array('_id' => new \MongoId($article->id)), array('locked' => LockMode::PESSIMISTIC_WRITE)); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->remove($article); + $this->dm->flush(); + } + + public function testPessimisticWriteLockThrowExceptionOnUpdate() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt'); + $coll->update(array('_id' => new \MongoId($article->id)), array('locked' => LockMode::PESSIMISTIC_WRITE)); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $article->title = 'changed'; + $this->dm->flush(); + } + + public function testPessimisticWriteLockThrowExceptionOnRead() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $coll = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt'); + $coll->update(array('_id' => new \MongoId($article->id)), array('locked' => LockMode::PESSIMISTIC_WRITE)); + + $this->setExpectedException('Doctrine\ODM\MongoDB\LockException'); + + $this->dm->clear(); + $article = $this->dm->find(__NAMESPACE__.'\LockInt', $article->id); + } + + public function testPessimisticReadLockFunctional() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::PESSIMISTIC_READ); + + $article->title = 'test'; + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt')->findOne(); + $this->assertEquals(2, $check['version']); + $this->assertFalse(isset($check['locked'])); + $this->assertEquals('test', $check['title']); + } + + public function testPessimisticWriteLockFunctional() + { + $article = new LockInt(); + $article->title = "my article"; + + $this->dm->persist($article); + $this->dm->flush(); + + $this->dm->lock($article, LockMode::PESSIMISTIC_WRITE); + + $article->title = 'test'; + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(__NAMESPACE__.'\LockInt')->findOne(); + $this->assertEquals(2, $check['version']); + $this->assertFalse(isset($check['locked'])); + $this->assertEquals('test', $check['title']); + } + + public function testInvalidLockDocument() + { + $this->setExpectedException('Doctrine\ODM\MongoDB\MongoDBException', 'Invalid lock field type string. Lock field must be int.'); + $this->dm->getClassMetadata(__NAMESPACE__.'\InvalidLockDocument'); + } + + public function testInvalidVersionDocument() + { + $this->setExpectedException('Doctrine\ODM\MongoDB\MongoDBException', 'Invalid version field type string. Version field must be int or date.'); + $this->dm->getClassMetadata(__NAMESPACE__.'\InvalidVersionDocument'); + } +} + +/** @ODM\MappedSuperclass */ +abstract class AbstractVersionBase +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $title; + + /** @ODM\Lock @ODM\Int */ + public $locked; + + public function __construct($title = null) + { + $this->title = $title; + } + + public function getId() + { + return $this->id; + } + + public function getTitle() + { + return $title; + } + + public function getVersion() + { + return $this->version; + } +} + +/** @ODM\Document */ +class LockInt extends AbstractVersionBase +{ + /** @ODM\Version @ODM\Int */ + public $version = 1; +} + +/** @ODM\Document */ +class LockTimestamp extends AbstractVersionBase +{ + /** @ODM\Version @ODM\Date */ + public $version; +} + +/** @ODM\Document */ +class InvalidLockDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Lock @ODM\String */ + public $lock; +} + +/** @ODM\Document */ +class InvalidVersionDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Version @ODM\String */ + public $version; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MapReduceTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MapReduceTest.php new file mode 100644 index 00000000..69adfc4e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MapReduceTest.php @@ -0,0 +1,257 @@ + + */ +class MapReduceTest extends \PHPUnit_Framework_TestCase +{ + protected $dm; + + public function setUp() + { + $config = new Configuration(); + + $config->setProxyDir(__DIR__ . '/../../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $config->setDefaultDB('doctrine_odm_tests'); + + $reader = new AnnotationReader(); + $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/Documents')); + + $this->dm = DocumentManager::create(new Connection(), $config); + + $currencies = array('USD' => 1, 'EURO' => 1.7, 'JPN' => 0.0125); + + foreach ($currencies as $name => &$multiplier) { + $multiplier = new Currency($name, $multiplier); + $this->dm->persist($multiplier); + } + + $stockItems = array( + new StockItem('stock_item_0', new Money(9.99 * 0 + 5, $currencies['USD']), 5), + new StockItem('stock_item_1', new Money(9.99 * 1 + 5, $currencies['USD']), 15 * 1 - 4), + new StockItem('stock_item_2', new Money(9.99 * 2 + 5, $currencies['USD']), 15 * 2 - 4), + new StockItem('stock_item_3', new Money(9.99 * 3 + 5, $currencies['USD']), 15 * 3 - 4), + new StockItem('stock_item_4', new Money(9.99 * 4 + 5, $currencies['USD']), 15 * 4 - 4), + new StockItem('stock_item_5', new Money(9.99 * 5 + 5, $currencies['USD']), 15 * 5 - 4), + new StockItem('stock_item_6', new Money(9.99 * 6 + 5, $currencies['USD']), 15 * 6 - 4), + new StockItem('stock_item_7', new Money(9.99 * 7 + 5, $currencies['USD']), 15 * 7 - 4), + new StockItem('stock_item_8', new Money(9.99 * 8 + 5, $currencies['USD']), 15 * 8 - 4), + new StockItem('stock_item_9', new Money(9.99 * 9 + 5, $currencies['USD']), 15 * 9 - 4), + ); + + $options = array( + new Option('option_0', new Money(13.99, $currencies['USD']), $stockItems[0]), + new Option('option_1', new Money(14.99, $currencies['USD']), $stockItems[1]), + new Option('option_2', new Money(15.99, $currencies['USD']), $stockItems[2]), + new Option('option_3', new Money(16.99, $currencies['USD']), $stockItems[3]), + new Option('option_4', new Money(17.99, $currencies['USD']), $stockItems[4]), + new Option('option_5', new Money(18.99, $currencies['USD']), $stockItems[5]), + new Option('option_6', new Money(19.99, $currencies['USD']), $stockItems[6]), + new Option('option_7', new Money(20.99, $currencies['USD']), $stockItems[7]), + new Option('option_8', new Money(21.99, $currencies['USD']), $stockItems[8]), + new Option('option_9', new Money(22.99, $currencies['USD']), $stockItems[9]), + ); + + $products = array( + new ConfigurableProduct('product_0'), + new ConfigurableProduct('product_1'), + new ConfigurableProduct('product_2'), + new ConfigurableProduct('product_3'), + new ConfigurableProduct('product_4'), + new ConfigurableProduct('product_5'), + new ConfigurableProduct('product_6'), + new ConfigurableProduct('product_7'), + new ConfigurableProduct('product_8'), + new ConfigurableProduct('product_9'), + ); + + $products[0]->addOption($options[0]); + $products[0]->addOption($options[4]); + $products[0]->addOption($options[6]); + + $products[1]->addOption($options[1]); + $products[1]->addOption($options[2]); + $products[1]->addOption($options[5]); + $products[1]->addOption($options[7]); + $products[1]->addOption($options[8]); + + $products[2]->addOption($options[3]); + $products[2]->addOption($options[5]); + $products[2]->addOption($options[7]); + $products[2]->addOption($options[9]); + + $products[3]->addOption($options[0]); + $products[3]->addOption($options[1]); + $products[3]->addOption($options[2]); + $products[3]->addOption($options[3]); + $products[3]->addOption($options[4]); + $products[3]->addOption($options[5]); + + $products[4]->addOption($options[4]); + $products[4]->addOption($options[7]); + $products[4]->addOption($options[2]); + $products[4]->addOption($options[8]); + + $products[5]->addOption($options[9]); + + $products[6]->addOption($options[7]); + $products[6]->addOption($options[8]); + $products[6]->addOption($options[9]); + + $products[7]->addOption($options[4]); + $products[7]->addOption($options[5]); + + $products[8]->addOption($options[2]); + + $products[9]->addOption($options[4]); + $products[9]->addOption($options[3]); + $products[9]->addOption($options[7]); + + foreach ($products as $product) { + $this->dm->persist($product); + } + $this->dm->flush(); + $this->dm->clear(); + } + + public function tearDown() + { + $documents = array( + 'Documents\Ecommerce\ConfigurableProduct', + 'Documents\Ecommerce\StockItem', + 'Documents\Ecommerce\Currency', + 'Documents\User', + 'Documents\Event' + ); + foreach ($documents as $document) { + $this->dm->getDocumentCollection($document)->drop(); + } + } + + public function testMapReduce() + { + $map = 'function() { + for(i = 0; i <= this.options.length; i++) { + emit(this.name, { count: 1 }); + } + }'; + + $reduce = 'function(product, values) { + var total = 0 + values.forEach(function(value){ + total+= value.count; + }); + return { + product: product, + options: total, + test: values + }; + }'; + + $cursor = $this->dm->createQueryBuilder('Documents\Ecommerce\ConfigurableProduct') + ->map($map)->reduce($reduce) + ->getQuery()->execute(); + $this->assertEquals(10, $cursor->count()); + + $qb = $this->dm->createQueryBuilder('Documents\Ecommerce\ConfigurableProduct') + ->mapReduce($map, $reduce); + $query = $qb->getQuery(); + $cursor = $query->execute(); + $this->assertEquals(10, $cursor->count()); + $results = $cursor->toArray(); + $this->assertTrue(is_array($results[0])); + } + + public function testMapReduce2() + { + $user = new User(); + $user->setUsername('bob'); + + $event1 = new Event(); + $event1->setUser($user); + $event1->setType('sale'); + $event1->setTitle('Test 1'); + + $event2 = new Event(); + $event2->setUser($user); + $event2->setType('sale'); + $event2->setTitle('Test 2'); + + $event3 = new Event(); + $event3->setUser($user); + $event3->setType('sale'); + $event3->setTitle('Test 2'); + + $this->dm->persist($user); + $this->dm->persist($event1); + $this->dm->persist($event2); + $this->dm->persist($event3); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\Event') + ->field('type') + ->equals('sale') + ->map('function() { emit(this.user.$id, 1); }') + ->reduce("function(k, vals) { + var sum = 0; + for (var i in vals) { + sum += vals[i]; + } + return sum; + }"); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + + $this->assertEquals($user->getId(), (string) $user2['_id']); + $this->assertEquals(3, $user2['value']); + } + + /** + * @expectedException RuntimeException + */ + public function testMapReduceError() + { + $map = 'function() { + for(i = 0; i <= this.options.length; i++) { + emit(this.name.fetch(), { count: 1 }); + } + }'; + + $reduce = 'function(product, values) { + var total = 0 + values.forEach(function(value){ + total += value.count; + }); + return { + product: product, + options: total, + test: values + }; + }'; + + $results = $this->dm->createQueryBuilder('Documents\Ecommerce\ConfigurableProduct') + ->map($map)->reduce($reduce) + ->getQuery()->execute(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MappedSuperclassTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MappedSuperclassTest.php new file mode 100644 index 00000000..9d7edb7a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/MappedSuperclassTest.php @@ -0,0 +1,143 @@ +setId(1); + $e->setName('Roman'); + $e->setMapped1(42); + $e->setMapped2('bar'); + + $related = new MappedSuperclassRelated1(); + $related->setId(1); + $related->setName('Related'); + $e->setMappedRelated1($related); + + $this->dm->persist($related); + $this->dm->persist($e); + $this->dm->flush(); + $this->dm->clear(); + + $e2 = $this->dm->find('Doctrine\ODM\MongoDB\Tests\Functional\DocumentSubClass', 1); + $this->assertNotNull($e2); + $this->assertEquals(1, $e2->getId()); + $this->assertEquals('Roman', $e2->getName()); + $this->assertNotNull($e2->getMappedRelated1()); + $this->assertInstanceOf(__NAMESPACE__.'\MappedSuperclassRelated1', $e2->getMappedRelated1()); + $this->assertEquals(42, $e2->getMapped1()); + $this->assertEquals('bar', $e2->getMapped2()); + } +} + +/** @ODM\MappedSuperclass */ +class MappedSuperclassBase +{ + /** @ODM\String */ + private $mapped1; + + /** @ODM\String */ + private $mapped2; + + /** + * @ODM\ReferenceOne(targetDocument="MappedSuperclassRelated1") + */ + private $mappedRelated1; + + private $transient; + + public function setMapped1($val) + { + $this->mapped1 = $val; + } + + public function getMapped1() + { + return $this->mapped1; + } + + public function setMapped2($val) + { + $this->mapped2 = $val; + } + + public function getMapped2() + { + return $this->mapped2; + } + + public function setMappedRelated1($mappedRelated1) + { + $this->mappedRelated1 = $mappedRelated1; + } + + public function getMappedRelated1() + { + return $this->mappedRelated1; + } +} + +/** @ODM\Document */ +class MappedSuperclassRelated1 +{ + /** @ODM\Id(strategy="none") */ + private $id; + + /** @ODM\String */ + private $name; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } +} + +/** @ODM\Document */ +class DocumentSubClass extends MappedSuperclassBase +{ + /** @ODM\Id(strategy="none") */ + private $id; + + /** @ODM\String */ + private $name; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedDocumentsTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedDocumentsTest.php new file mode 100644 index 00000000..93f83e05 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedDocumentsTest.php @@ -0,0 +1,292 @@ +title = 'Product'; + + $order = new Order(); + $order->title = 'Order'; + + $this->dm->persist($product); + $this->dm->persist($order); + $this->dm->flush(); + + $productBackup = new ProductBackup(); + $productBackup->title = $product->title; + $productBackup->id = $product->id; + $order->product = $productBackup; + + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\Order')->findOne(); + + $this->assertInstanceOf('\MongoId', $test['product']['_id']); + $this->assertEquals('Order', $test['title']); + $this->assertEquals('Product', $test['product']['title']); + + $doc = $this->dm->find(__NAMESPACE__.'\Order', $order->id); + $this->assertInstanceOf(__NAMESPACE__.'\Order', $order); + $this->assertTrue(is_string($doc->product->id)); + $this->assertEquals((string) $test['product']['_id'], $doc->product->id); + $this->assertEquals('Order', $doc->title); + $this->assertEquals('Product', $doc->product->title); + + $this->dm->clear(); + + $order = $this->dm->find(__NAMESPACE__.'\Order', $order->id); + $this->assertInstanceOf(__NAMESPACE__.'\Order', $order); + + $product = $this->dm->find(__NAMESPACE__.'\Product', $product->id); + $this->assertInstanceOf(__NAMESPACE__.'\Product', $product); + + $order->product->title = 'tesgttttt'; + $this->dm->flush(); + $this->dm->clear(); + + $test1 = $this->dm->getDocumentCollection(__NAMESPACE__.'\Product')->findOne(); + $test2 = $this->dm->getDocumentCollection(__NAMESPACE__.'\Order')->findOne(); + $this->assertNotEquals($test1['title'], $test2['product']['title']); + + $order = $this->dm->find(__NAMESPACE__.'\Order', $order->id); + $product = $this->dm->find(__NAMESPACE__.'\Product', $product->id); + $this->assertNotEquals($product->title, $order->product->title); + } + + public function testNestedCategories() + { + $category = new Category('Root'); + $child1 = $category->addChild('Child 1'); + $child2 = $child1->addChild('Child 2'); + $this->dm->persist($category); + $this->dm->flush(); + $this->dm->clear(); + + $category = $this->dm->find(__NAMESPACE__.'\Category', $category->getId()); + $this->assertNotNull($category); + $category->setName('Root Changed'); + $children = $category->getChildren(); + + $children[0]->setName('Child 1 Changed'); + $children[0]->getChild('Child 2')->setName('Child 2 Changed'); + $category->addChild('Child 2'); + $this->dm->flush(); + $this->dm->clear(); + + $category = $this->dm->find(__NAMESPACE__.'\Category', $category->getId()); + + $children = $category->getChildren(); + $this->assertEquals('Child 1 Changed', $children[0]->getName()); + $this->assertEquals('Child 2 Changed', $children[0]->getChild(0)->getName()); + $this->assertEquals('Root Changed', $category->getName()); + $this->assertEquals(2, count($category->getChildren())); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\Category')->findOne(); + $this->assertFalse(isset($test['children'][0]['children'][0]['children'])); + } + + public function testNestedReference() + { + $test = new Hierarchy('Root'); + $child1 = $test->addChild('Child 1'); + $child2 = $test->addChild('Child 2'); + $this->dm->persist($child1); + $this->dm->persist($child2); + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository(__NAMESPACE__.'\Hierarchy')->findOneBy(array('name' => 'Root')); + + $this->assertNotNull($test); + $child1 = $test->getChild('Child 1')->setName('Child 1 Changed'); + $child2 = $test->getChild('Child 2')->setName('Child 2 Changed'); + $test->setName('Root Changed'); + $child3 = $test->addChild('Child 3'); + $this->dm->persist($child3); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find(__NAMESPACE__.'\Hierarchy', $test->getId()); + $this->assertNotNull($test); + $this->assertEquals('Root Changed', $test->getName()); + $this->assertEquals('Child 1 Changed', $test->getChild(0)->getName()); + $this->assertEquals('Child 2 Changed', $test->getChild(1)->getName()); + + $child3 = $this->dm->getRepository(__NAMESPACE__.'\Hierarchy')->findOneBy(array('name' => 'Child 3')); + $this->assertNotNull($child3); + $child3->setName('Child 3 Changed'); + $this->dm->flush(); + + $child3 = $this->dm->getRepository(__NAMESPACE__.'\Hierarchy')->findOneBy(array('name' => 'Child 3 Changed')); + $this->assertNotNull($child3); + $this->assertEquals('Child 3 Changed', $child3->getName()); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\Hierarchy')->findOne(array('name' => 'Child 1 Changed')); + $this->assertFalse(isset($test['children']), 'Test empty array is not stored'); + } +} + +/** @ODM\Document */ +class Hierarchy +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; + + /** @ODM\ReferenceMany(targetDocument="Hierarchy") */ + private $children = array(); + + public function __construct($name) + { + $this->name = $name; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getChild($name) + { + if (is_numeric($name)) { + return $this->children[$name]; + } + foreach ($this->children as $child) { + if ($child->name === $name) { + return $child; + } + } + return null; + } + + public function addChild($child) + { + if (is_string($child)) { + $child = new Hierarchy($child); + } + $this->children[] = $child; + return $child; + } + + public function getChildren() + { + return $this->children; + } +} + +/** @ODM\MappedSuperclass */ +class BaseCategory +{ + /** @ODM\String */ + protected $name; + + /** @ODM\EmbedMany(targetDocument="ChildCategory") */ + protected $children = array(); + + public function __construct($name) + { + $this->name = $name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getChild($name) + { + if (is_numeric($name)) { + return $this->children[$name]; + } + foreach ($this->children as $child) { + if ($child->name === $name) { + return $child; + } + } + return null; + } + + public function addChild($child) + { + if (is_string($child)) { + $child = new ChildCategory($child); + } + $this->children[] = $child; + return $child; + } + + public function getChildren() + { + return $this->children; + } +} + +/** @ODM\Document */ +class Category extends BaseCategory +{ + /** @ODM\Id */ + protected $id; + + public function getId() + { + return $this->id; + } +} + +/** @ODM\EmbeddedDocument */ +class ChildCategory extends BaseCategory +{ +} + +/** @ODM\Document */ +class Order +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $title; + + /** @ODM\EmbedOne(targetDocument="ProductBackup") */ + public $product; +} + +/** @ODM\Document */ +class Product +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $title; +} + +/** @ODM\EmbeddedDocument */ +class ProductBackup extends Product +{ +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php new file mode 100644 index 00000000..c1944850 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php @@ -0,0 +1,259 @@ +name = 'Jon Wage'; + $customer->cart = new \Documents\Cart; + $customer->cart->numItems = 5; + $customer->cart->customer = $customer; + $customer->cartTest = 'test'; + $this->dm->persist($customer); + $this->dm->persist($customer->cart); + $this->dm->flush(); + $this->dm->clear(); + + $customer = $this->dm->getRepository('Documents\Customer')->find($customer->id); + $this->assertInstanceOf('Documents\Cart', $customer->cart); + $this->assertEquals($customer->cart->id, $customer->cart->id); + + $check = $this->dm->getDocumentCollection(get_class($customer))->findOne(); + $this->assertTrue(isset($check['cart'])); + $this->assertEquals('test', $check['cart']); + + $customer->cart = null; + $customer->cartTest = 'ok'; + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getDocumentCollection(get_class($customer))->findOne(); + $this->assertTrue(isset($check['cart'])); + $this->assertEquals('ok', $check['cart']); + + $customer = $this->dm->getRepository('Documents\Customer')->find($customer->id); + $this->assertInstanceOf('Documents\Cart', $customer->cart); + $this->assertEquals('ok', $customer->cartTest); + } + + public function testOneToManyBiDirectional() + { + $product = new \Documents\Product('Book'); + $product->addFeature(new \Documents\Feature('Pages')); + $product->addFeature(new \Documents\Feature('Cover')); + $this->dm->persist($product); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getDocumentCollection(get_class($product))->findOne(); + $this->assertFalse(isset($check['tags'])); + + $check = $this->dm->getDocumentCollection('Documents\Feature')->findOne(); + $this->assertTrue(isset($check['product'])); + + $product = $this->dm->createQueryBuilder(get_class($product)) + ->getQuery() + ->getSingleResult(); + $features = $product->features; + $this->assertEquals(2, count($features)); + $this->assertEquals('Pages', $features[0]->name); + $this->assertEquals('Cover', $features[1]->name); + } + + public function testOneToManySelfReferencing() + { + $node = new \Documents\BrowseNode('Root'); + $node->addChild(new \Documents\BrowseNode('Child 1')); + $node->addChild(new \Documents\BrowseNode('Child 2')); + + $this->dm->persist($node); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getDocumentCollection(get_class($node))->findOne(array('parent' => array('$exists' => false))); + $this->assertNotNull($check); + $this->assertFalse(isset($check['children'])); + + $root = $this->dm->createQueryBuilder(get_class($node)) + ->field('children')->exists(false) + ->getQuery() + ->getSingleResult(); + $this->assertInstanceOf('Documents\BrowseNode', $root); + $this->assertEquals(2, count($root->children)); + + unset($root->children[0]); + $this->dm->flush(); + + $this->assertEquals(1, count($root->children)); + + $this->dm->refresh($root); + $this->assertEquals(2, count($root->children)); + } + + public function testManyToMany() + { + $baseballTag = new \Documents\Tag('baseball'); + $blogPost = new \Documents\BlogPost(); + $blogPost->name = 'Test'; + $blogPost->addTag($baseballTag); + + $this->dm->persist($blogPost); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->getDocumentCollection(get_class($blogPost))->findOne(); + $this->assertEquals(1, count($check['tags'])); + + $check = $this->dm->getDocumentCollection('Documents\Tag')->findOne(); + $this->assertFalse(isset($check['blogPosts'])); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + $this->assertEquals(1, count($blogPost->tags)); + + $this->dm->clear(); + + $tag = $this->dm->createQueryBuilder('Documents\Tag') + ->getQuery() + ->getSingleResult(); + $this->assertEquals('baseball', $tag->name); + $this->assertEquals(1, $tag->blogPosts->count()); + $this->assertEquals('Test', $tag->blogPosts[0]->name); + } + + public function testManyToManySelfReferencing() + { + $jwage = new \Documents\FriendUser('jwage'); + $fabpot = new \Documents\FriendUser('fabpot'); + $fabpot->addFriend($jwage); + $romanb = new \Documents\FriendUser('romanb'); + $romanb->addFriend($jwage); + $jwage->addFriend($fabpot); + $jwage->addFriend($romanb); + + $this->dm->persist($jwage); + $this->dm->persist($fabpot); + $this->dm->persist($romanb); + $this->dm->flush(); + $this->dm->clear(); + + $check = $this->dm->createQueryBuilder('Documents\FriendUser') + ->field('name')->equals('fabpot') + ->hydrate(false) + ->getQuery() + ->getSingleResult(); + $this->assertFalse(isset($check['friendsWithMe'])); + + $user = $this->dm->createQueryBuilder('Documents\FriendUser') + ->field('name')->equals('fabpot') + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(1, count($user->friendsWithMe)); + $this->assertEquals('jwage', $user->friendsWithMe[0]->name); + + $this->dm->clear(); + + $user = $this->dm->createQueryBuilder('Documents\FriendUser') + ->field('name')->equals('romanb') + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(1, count($user->friendsWithMe)); + $this->assertEquals('jwage', $user->friendsWithMe[0]->name); + + $this->dm->clear(); + + $user = $this->dm->createQueryBuilder('Documents\FriendUser') + ->field('name')->equals('jwage') + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(2, count($user->myFriends)); + $this->assertEquals('fabpot', $user->myFriends[0]->name); + $this->assertEquals('romanb', $user->myFriends[1]->name); + + $this->assertEquals(2, count($user->friendsWithMe)); + $this->assertEquals('fabpot', $user->friendsWithMe[0]->name); + $this->assertEquals('romanb', $user->friendsWithMe[1]->name); + + $this->dm->clear(); + } + + public function testSortLimitAndSkipReferences() + { + $date1 = new DateTime(); + $date1->setTimestamp(strtotime('-20 seconds')); + + $date2 = new DateTime(); + $date2->setTimestamp(strtotime('-10 seconds')); + + $blogPost = new \Documents\BlogPost('Test'); + $blogPost->addComment(new \Documents\Comment('Comment 1', $date1)); + $blogPost->addComment(new \Documents\Comment('Comment 2', $date2)); + + $this->dm->persist($blogPost); + $this->dm->flush(); + $this->dm->clear(); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + $this->assertEquals('Comment 1', $blogPost->comments[0]->text); + $this->assertEquals('Comment 2', $blogPost->comments[1]->text); + $this->assertEquals('Test', $blogPost->comments[0]->parent->name); + $this->assertEquals('Test', $blogPost->comments[1]->parent->name); + + $this->dm->clear(); + + $comment = $this->dm->createQueryBuilder('Documents\Comment') + ->getQuery() + ->getSingleResult(); + $this->assertEquals('Test', $comment->parent->getName()); + + $this->dm->clear(); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + $this->assertEquals('Comment 1', $blogPost->firstComment->getText()); + $this->assertEquals('Comment 2', $blogPost->latestComment->getText()); + $this->assertEquals(2, count($blogPost->last5Comments)); + + $this->assertEquals('Comment 2', $blogPost->last5Comments[0]->getText()); + $this->assertEquals('Comment 1', $blogPost->last5Comments[1]->getText()); + + $this->dm->clear(); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + + $blogPost->addComment(new \Documents\Comment('Comment 3 by admin', $date1, true)); + $blogPost->addComment(new \Documents\Comment('Comment 4 by admin', $date2, true)); + $this->dm->flush(); + $this->dm->clear(); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + $this->assertEquals(2, count($blogPost->adminComments)); + $this->assertEquals('Comment 4 by admin', $blogPost->adminComments[0]->getText()); + $this->assertEquals('Comment 3 by admin', $blogPost->adminComments[1]->getText()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php new file mode 100644 index 00000000..8bff8f73 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php @@ -0,0 +1,117 @@ +username = "beberlei"; + $user1->name = "Benjamin"; + $user1->status = "active"; + $group1 = new CmsGroup(); + $group1->name = "test"; + $group2 = new CmsGroup(); + $group2->name = "test"; + $user1->addGroup($group1); + $user1->addGroup($group2); + $user2 = new CmsUser(); + $user2->username = "romanb"; + $user2->name = "Roman"; + $user2->status = "active"; + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->persist($group1); + $this->dm->persist($group2); + $this->dm->flush(); + $this->dm->clear(); + + $this->user1 = $this->dm->find(get_class($user1), $user1->id); + $this->user2 = $this->dm->find(get_class($user1), $user2->id); + } + + public function testClonePersistentCollectionAndReuse() + { + $user1 = $this->user1; + + $user1->groups = clone $user1->groups; + + $this->dm->flush(); + $this->dm->clear(); + + $user1 = $this->dm->find(get_class($user1), $user1->id); + + $this->assertEquals(2, count($user1->groups)); + } + + public function testClonePersistentCollectionAndShare() + { + $user1 = $this->user1; + $user2 = $this->user2; + + $user2->groups = clone $user1->groups; + + $this->dm->flush(); + $this->dm->clear(); + + $user1 = $this->dm->find(get_class($user1), $user1->id); + $user2 = $this->dm->find(get_class($user1), $user2->id); + + $this->assertEquals(2, count($user1->groups)); + $this->assertEquals(2, count($user2->groups)); + } + + public function testCloneThenDirtyPersistentCollection() + { + $user1 = $this->user1; + $user2 = $this->user2; + + $group3 = new CmsGroup(); + $group3->name = "test"; + $user2->groups = clone $user1->groups; + $user2->groups->add($group3); + + $this->dm->persist($group3); + $this->dm->flush(); + $this->dm->clear(); + + $user1 = $this->dm->find(get_class($user1), $user1->id); + $user2 = $this->dm->find(get_class($user1), $user2->id); + + $this->assertEquals(3, count($user2->groups)); + $this->assertEquals(2, count($user1->groups)); + } + + public function testNotCloneAndPassAroundFlush() + { + $user1 = $this->user1; + $user2 = $this->user2; + + $group3 = new CmsGroup(); + $group3->name = "test"; + $user2->groups = $user1->groups; + $user2->groups->add($group3); + + $this->assertEQuals(1, count($user1->groups->getInsertDiff())); + + $this->dm->persist($group3); + $this->dm->flush(); + $this->dm->clear(); + + $user1 = $this->dm->find(get_class($user1), $user1->id); + $user2 = $this->dm->find(get_class($user1), $user2->id); + + $this->assertEquals(3, count($user2->groups)); + $this->assertEquals(3, count($user1->groups)); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistingTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistingTest.php new file mode 100644 index 00000000..dcdd673d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistingTest.php @@ -0,0 +1,86 @@ +setName('Jon Test Account'); + + $user = new User(); + $user->setUsername('jon'); + $user->setPassword('changeme'); + $user->setAccount($account); + + $this->dm->persist($user); + $this->dm->flush(); + + $account->setName('w00t'); + $this->dm->flush(); + + $this->assertEquals('w00t', $user->getAccount()->getName()); + + $this->dm->remove($user); + $this->dm->flush(); + $this->dm->clear(); + } + + public function testUpdate() + { + $user = new User(); + $user->setInheritedProperty('cool'); + $user->setUsername('jon'); + $user->setPassword('changeme'); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + + $this->assertNotNull($user); + $user->setUsername('w00t'); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertNotNull($user); + $this->assertEquals('w00t', $user->getUsername()); + $this->assertEquals('cool', $user->getInheritedProperty()); + } + + public function testDetach() + { + $user = new User(); + $user->setUsername('jon'); + $user->setPassword('changeme'); + $this->dm->persist($user); + $this->dm->flush(); + + $user->setUsername('whoop'); + $this->dm->detach($user); + $this->dm->flush(); + $this->dm->clear(); + + $user2 = $this->dm->find('Documents\User', $user->getId()); + $this->assertNotNull($user2); + $this->assertEquals('jon', $user2->getUsername()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrePersistTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrePersistTest.php new file mode 100644 index 00000000..bd3bbd4b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrePersistTest.php @@ -0,0 +1,51 @@ +dm->persist($test); + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->flush(); + + $this->assertEquals(1, $test->prePersist); + + $test->field = 'test'; + + $this->dm->flush(); + $this->dm->flush(); + + $this->assertEquals(1, $test->preUpdate); + } +} + +/** @ODM\Document */ +class PrePersistTestDocument +{ + public $prePersist; + public $preUpdate; + + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $field; + + /** @ODM\PrePersist */ + public function prePersist() + { + $this->prePersist++; + } + + /** @ODM\PreUpdate */ + public function preUpdate() + { + $this->preUpdate++; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrimeTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrimeTest.php new file mode 100644 index 00000000..b431b842 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/PrimeTest.php @@ -0,0 +1,58 @@ +setGroups(new ArrayCollection(array($group1, $group2))); + $account1 = new \Documents\Account(); + $user1->setAccount($account1); + + $user2 = new \Documents\User(); + $user2->setGroups(new ArrayCollection(array($group1, $group2))); + $account2 = new \Documents\Account(); + $user2->setAccount($account2); + + $this->dm->persist($group1); + $this->dm->persist($group2); + $this->dm->persist($account1); + $this->dm->persist($user1); + $this->dm->persist($account2); + $this->dm->persist($user2); + + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups')->prime(true) + ->field('account')->prime(true); + + $query = $qb->getQuery(); + $users = $query->execute(); + foreach ($users as $user) { + $this->assertTrue($user->getAccount()->__isInitialized__); + foreach ($user->getGroups() as $group) { + $this->assertNotEquals('Proxies\DocumentsGroupProxy', get_class($group)); + } + } + + $this->dm->clear(); + + $test = false; + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups')->prime(function() use (&$test) { + $test = true; + }); + + $query = $qb->getQuery(); + $users = $query->execute(); + $this->assertTrue($test); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php new file mode 100644 index 00000000..ef120cab --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php @@ -0,0 +1,405 @@ +user = new User(); + $this->user->setUsername('boo'); + + $this->dm->persist($this->user); + $this->dm->flush(); + } + + public function testAddElemMatch() + { + $user = new User(); + $user->setUsername('boo'); + $phonenumber = new Phonenumber('6155139185'); + $user->addPhonenumber($phonenumber); + + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $embeddedQb = $this->dm->createQueryBuilder('Documents\Phonenumber'); + + $qb->field('phonenumbers')->elemMatch($embeddedQb->expr()->field('phonenumber')->equals('6155139185')); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNotNull($user); + } + + public function testAddElemMatchWithDeepFields() + { + $user1 = new User(); + $user1->setUsername('ben'); + + $user2 = new User(); + $user2->setUsername('boo'); + $phonenumber = new Phonenumber('2125550123', $user1); + $user2->addPhonenumber($phonenumber); + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $embeddedQb = $this->dm->createQueryBuilder('Documents\Phonenumber'); + + $qb->field('phonenumbers')->elemMatch($embeddedQb->expr()->field('lastCalledBy.$id')->equals(new \MongoId($user1->getId()))); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNotNull($user); + } + + public function testAddNot() + { + $user = new User(); + $user->setUsername('boo'); + + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $qb->field('username')->not($qb->expr()->in(array('boo'))); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNull($user); + + $qb->field('username')->not($qb->expr()->in(array('1boo'))); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNotNull($user); + } + + public function testDistinct() + { + $user = new User(); + $user->setUsername('distinct_test'); + $user->setCount(1); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('distinct_test'); + $user->setCount(1); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('distinct_test'); + $user->setCount(2); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('distinct_test'); + $user->setCount(3); + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->distinct('count') + ->field('username')->equals('distinct_test'); + $q = $qb->getQuery(); + $results = $q->execute(); + $this->assertEquals(new \Doctrine\MongoDB\ArrayIterator(array(1, 2, 3)), $results); + + $results = $this->dm->createQueryBuilder('Documents\User') + ->distinct('count') + ->field('username')->equals('distinct_test') + ->getQuery() + ->execute(); + $this->assertEquals(new \Doctrine\MongoDB\ArrayIterator(array(1, 2, 3)), $results); + } + + public function testFindQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->where("function() { return this.username == 'boo' }"); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertEquals('boo', $user->getUsername()); + } + + public function testUpdateQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->field('username') + ->set('crap') + ->equals('boo'); + $query = $qb->getQuery(); + $result = $query->execute(); + + $this->dm->refresh($this->user); + $this->assertEquals('crap', $this->user->getUsername()); + } + + public function testUpsertUpdateQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->upsert(true) + ->field('username') + ->set('crap') + ->equals('foo'); + $query = $qb->getQuery(); + $result = $query->execute(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->find() + ->field('username')->equals('crap'); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNotNull($user); + } + + public function testMultipleUpdateQuery() + { + $user = new User(); + $user->setUsername('multiple_test'); + $user->setCount(1); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('multiple_test'); + $user->setCount(1); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('multiple_test'); + $user->setCount(2); + $this->dm->persist($user); + + $user = new User(); + $user->setUsername('multiple_test'); + $user->setCount(3); + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->multiple() + ->field('username')->equals('multiple_test') + ->field('username')->set('foo'); + $q = $qb->getQuery(); + $results = $q->execute(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->find() + ->field('username')->equals('foo'); + $q = $qb->getQuery(); + $users = array_values($q->execute()->toArray()); + + $this->assertEquals(4, count($users)); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testRemoveQuery() + { + $this->dm->remove($this->user); + + // should invoke exception because $this->user doesn't exist anymore + $this->dm->refresh($this->user); + } + + public function testIncUpdateQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->field('hits')->inc(5) + ->field('username')->equals('boo'); + $query = $qb->getQuery(); + $query->execute(); + $query->execute(); + + $qb->find('Documents\User') + ->hydrate(false); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertEquals(10, $user['hits']); + } + + public function testUnsetFieldUpdateQuery() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->field('hits')->unsetField() + ->field('username')->equals('boo'); + $query = $qb->getQuery(); + $result = $query->execute(); + + $qb->find('Documents\User') + ->hydrate(false); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertFalse(isset($user['hits'])); + } + + public function testGroup() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->group(array(), array('count' => 0)) + ->reduce('function (obj, prev) { prev.count++; }'); + $query = $qb->getQuery(); + $result = $query->execute(); + $this->assertEquals(1, $result['retval'][0]['count']); + } + + public function testUnsetField() + { + $qb = $this->dm->createQueryBuilder() + ->update('Documents\User') + ->field('nullTest') + ->type('null') + ->unsetField('nullTest'); + $query = $qb->getQuery(); + $query->execute(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('nullTest')->type('null'); + $query = $qb->getQuery(); + $user = $query->getSingleResult(); + $this->assertNull($user); + } + + public function testDateRange() + { + $article1 = new Article(); + $article1->setTitle('test'); + $article1->setBody('test'); + $article1->setCreatedAt('1985-09-01 00:00:00'); + + $article2 = new Article(); + $article2->setTitle('test'); + $article2->setBody('test'); + $article2->setCreatedAt('1985-09-02 00:00:00'); + + $article3 = new Article(); + $article3->setTitle('test'); + $article3->setBody('test'); + $article3->setCreatedAt('1985-09-03 00:00:00'); + + $article4 = new Article(); + $article4->setTitle('test'); + $article4->setBody('test'); + $article4->setCreatedAt('1985-09-04 00:00:00'); + + $this->dm->persist($article1); + $this->dm->persist($article2); + $this->dm->persist($article3); + $this->dm->persist($article4); + + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\Article'); + $qb->field('createdAt')->range( + new \MongoDate(strtotime('1985-09-01 01:00:00')), + new \MongoDate(strtotime('1985-09-04')) + ); + $query = $qb->getQuery(); + $articles = array_values($query->execute()->toArray()); + $this->assertEquals(2, count($articles)); + $this->assertEquals('1985-09-02', $articles[0]->getCreatedAt()->format('Y-m-d')); + $this->assertEquals('1985-09-03', $articles[1]->getCreatedAt()->format('Y-m-d')); + } + + public function testQueryIsIterable() + { + $article = new Article(); + $article->setTitle('test'); + $this->dm->persist($article); + $this->dm->flush(null, array('safe' => true)); + + $qb = $this->dm->createQueryBuilder('Documents\Article'); + $query = $qb->getQuery(); + $this->assertTrue($query instanceof \Doctrine\MongoDB\IteratorAggregate); + foreach ($query as $article) { + $this->assertEquals('Documents\Article', get_class($article)); + } + } + + public function testQueryReferences() + { + $group = new \Documents\Group('Test Group'); + + $user = new User(); + $user->setUsername('cool'); + $user->addGroup($group); + + $this->dm->persist($user); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups')->references($group); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + $this->assertSame($user, $user2); + } + + public function testQueryWhereIn() + { + $qb = $this->dm->createQueryBuilder('Documents\User'); + $choices = array('a', 'b'); + $qb->field('username')->in($choices); + $expected = array( + 'username' => array( + '$in' => $choices + ) + ); + $this->assertSame($expected, $qb->getQueryArray()); + } + + public function testQueryWhereInReferenceId() + { + $qb = $this->dm->createQueryBuilder('Documents\User'); + $choices = array(new \MongoId(), new \MongoId()); + $qb->field('account.$id')->in($choices); + $expected = array( + 'account.$id' => array( + '$in' => $choices + ) + ); + $this->assertSame($expected, $qb->getQueryArray()); + $this->assertSame($expected, $qb->getQuery()->debug()); + } + + // search for articles that have the "pet" tag in their tags collection + public function testQueryWhereOneValueOfCollection() + { + $qb = $this->dm->createQueryBuilder('Documents\Article'); + $qb->field('tags')->equals('pet'); + $expected = array( + 'tags' => 'pet' + ); + $this->assertSame($expected, $qb->getQueryArray()); + $this->assertSame($expected, $qb->getQuery()->debug()); + } + + // search for articles where tags exactly equal [pet, blue] + public function testQueryWhereAllValuesOfCollection() + { + $qb = $this->dm->createQueryBuilder('Documents\Article'); + $qb->field('tags')->equals(array('pet', 'blue')); + $expected = array( + 'tags' => array('pet', 'blue') + ); + $this->assertSame($expected, $qb->getQueryArray()); + $this->assertSame($expected, $qb->getQuery()->debug()); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php new file mode 100644 index 00000000..117ebcb5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php @@ -0,0 +1,47 @@ +raw = $value; + + $this->dm->persist($test); + $this->dm->flush(); + + $result = $this->dm->getDocumentCollection(get_class($test))->findOne(array('_id' => new \MongoId($test->id))); + $this->assertEquals($value, $result['raw']); + } + + public function getTestRawTypeData() + { + return array( + array('test'), + array(1), + array(0), + array(array('test' => 'test')), + array(new \MongoDate()), + array(true), + array(array('date' => new \MongoDate())), + array(new \MongoId()) + ); + } +} + +/** @ODM\Document */ +class RawType +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Raw */ + public $raw; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceDiscriminatorsTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceDiscriminatorsTest.php new file mode 100644 index 00000000..7916dd17 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceDiscriminatorsTest.php @@ -0,0 +1,225 @@ +dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__ . '\CommentableAction'); + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__ . '\GroupMainActivityStreamItem'); + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__ . '\GroupMembersActivityStreamItem'); + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__ . '\UserDashboardActivityStreamItem'); + $this->dm->getSchemaManager()->ensureDocumentIndexes(__NAMESPACE__ . '\UserProfileActivityStreamItem'); + } + + /** + * This test demonstrates a CommentableAction being published to activity streams. + */ + public function testReferenceDiscriminators() + { + $this->dm->persist($commentableAction = new CommentableAction('actionType')); + $this->dm->persist($groupMainActivityStreamItem = new GroupMainActivityStreamItem($commentableAction, 'groupId')); + $this->dm->persist($groupMemberActivityStreamItem = new GroupMembersActivityStreamItem($commentableAction, 'groupId')); + $this->dm->persist($userDashboardActivityStreamItem = new UserDashboardActivityStreamItem($commentableAction, 'userId')); + $this->dm->persist($userProfileActivityStreamItem = new UserProfileActivityStreamItem($commentableAction, 'userId')); + + $this->dm->flush(); + $this->dm->clear(); + + $commentableAction = $this->dm->find(__NAMESPACE__ . '\CommentableAction', $commentableAction->getId()); + $groupMainActivityStreamItem = $this->dm->find(__NAMESPACE__ . '\GroupMainActivityStreamItem', $groupMainActivityStreamItem->getId()); + $groupMemberActivityStreamItem = $this->dm->find(__NAMESPACE__ . '\GroupMembersActivityStreamItem', $groupMemberActivityStreamItem->getId()); + $userDashboardActivityStreamItem = $this->dm->find(__NAMESPACE__ . '\UserDashboardActivityStreamItem', $userDashboardActivityStreamItem->getId()); + $userProfileActivityStreamItem = $this->dm->find(__NAMESPACE__ . '\UserProfileActivityStreamItem', $userProfileActivityStreamItem->getId()); + + $this->assertSame($commentableAction, $groupMainActivityStreamItem->getAction()); + $this->assertSame($commentableAction, $groupMemberActivityStreamItem->getAction()); + $this->assertSame($commentableAction, $userDashboardActivityStreamItem->getAction()); + $this->assertSame($commentableAction, $userProfileActivityStreamItem->getAction()); + } + + /** + * This tests demonstrates a race condition between two requests which are + * both publishing a CommentableAction to activity streams. + */ + public function testReferenceDiscriminatorsRaceCondition() + { + $this->dm->persist($commentableAction1 = new CommentableAction('actionType')); + $this->dm->persist($groupMainActivityStreamItem1 = new GroupMainActivityStreamItem($commentableAction1, 'groupId')); + $this->dm->persist($groupMemberActivityStreamItem1 = new GroupMembersActivityStreamItem($commentableAction1, 'groupId')); + $this->dm->persist($userDashboardActivityStreamItem1 = new UserDashboardActivityStreamItem($commentableAction1, 'userId')); + $this->dm->persist($userProfileActivityStreamItem1 = new UserProfileActivityStreamItem($commentableAction1, 'userId')); + + $this->dm->persist($commentableAction2 = new CommentableAction('actionType')); + $this->dm->persist($groupMainActivityStreamItem2 = new GroupMainActivityStreamItem($commentableAction2, 'groupId')); + $this->dm->persist($groupMemberActivityStreamItem2 = new GroupMembersActivityStreamItem($commentableAction2, 'groupId')); + $this->dm->persist($userDashboardActivityStreamItem2 = new UserDashboardActivityStreamItem($commentableAction2, 'userId')); + $this->dm->persist($userProfileActivityStreamItem2 = new UserProfileActivityStreamItem($commentableAction2, 'userId')); + + $this->dm->flush(); + $this->dm->clear(); + + $commentableAction1 = $this->dm->find(__NAMESPACE__ . '\CommentableAction', $commentableAction1->getId()); + $groupMainActivityStreamItem1 = $this->dm->find(__NAMESPACE__ . '\GroupMainActivityStreamItem', $groupMainActivityStreamItem1->getId()); + $groupMemberActivityStreamItem1 = $this->dm->find(__NAMESPACE__ . '\GroupMembersActivityStreamItem', $groupMemberActivityStreamItem1->getId()); + $userDashboardActivityStreamItem1 = $this->dm->find(__NAMESPACE__ . '\UserDashboardActivityStreamItem', $userDashboardActivityStreamItem1->getId()); + $userProfileActivityStreamItem1 = $this->dm->find(__NAMESPACE__ . '\UserProfileActivityStreamItem', $userProfileActivityStreamItem1->getId()); + + $commentableAction2 = $this->dm->find(__NAMESPACE__ . '\CommentableAction', $commentableAction2->getId()); + $groupMainActivityStreamItem2 = $this->dm->find(__NAMESPACE__ . '\GroupMainActivityStreamItem', $groupMainActivityStreamItem2->getId()); + $groupMemberActivityStreamItem2 = $this->dm->find(__NAMESPACE__ . '\GroupMembersActivityStreamItem', $groupMemberActivityStreamItem2->getId()); + $userDashboardActivityStreamItem2 = $this->dm->find(__NAMESPACE__ . '\UserDashboardActivityStreamItem', $userDashboardActivityStreamItem2->getId()); + $userProfileActivityStreamItem2 = $this->dm->find(__NAMESPACE__ . '\UserProfileActivityStreamItem', $userProfileActivityStreamItem2->getId()); + + $this->assertSame($commentableAction1, $groupMainActivityStreamItem1->getAction()); + $this->assertSame($commentableAction1, $groupMemberActivityStreamItem1->getAction()); + $this->assertSame($commentableAction1, $userDashboardActivityStreamItem1->getAction()); + $this->assertSame($commentableAction1, $userProfileActivityStreamItem1->getAction()); + + $this->assertSame($commentableAction2, $groupMainActivityStreamItem2->getAction()); + $this->assertSame($commentableAction2, $groupMemberActivityStreamItem2->getAction()); + $this->assertSame($commentableAction2, $userDashboardActivityStreamItem2->getAction()); + $this->assertSame($commentableAction2, $userProfileActivityStreamItem2->getAction()); + } +} + +/** +* @ODM\Document(collection="rdt_action") +* @ODM\InheritanceType("SINGLE_COLLECTION") +* @ODM\DiscriminatorField(fieldName="discriminator") +* @ODM\DiscriminatorMap({"action"="Action", "commentable_action"="CommentableAction"}) +*/ +class Action +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $type; + + public function __construct($type) + { + $this->type = $type; + } + + public function getId() + { + return $this->id; + } + + public function getType() + { + return $this->type; + } +} + +/** @ODM\Document */ +class CommentableAction extends Action +{ + /** + * @ODM\Collection + **/ + protected $comments = array(); + + public function __construct($type, array $comments = array()) + { + parent::__construct($type); + $this->comments = $comments; + } + + public function getComments() + { + return $this->comments; + } +} + +/** @ODM\MappedSuperclass */ +abstract class ActivityStreamItem +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\ReferenceOne(targetDocument="Action") */ + protected $action; + + public function __construct(Action $action) + { + $this->action = $action; + } + + public function getId() + { + return $this->id; + } + + public function getAction() + { + return $this->action; + } +} + +/** + * @ODM\MappedSuperclass + * @ODM\UniqueIndex(keys={"groupId"="asc", "action.$id"="asc"}, options={"unique"="true", "dropDups"="true"}) + */ +abstract class GroupActivityStreamItem extends ActivityStreamItem +{ + /** @ODM\String */ + protected $groupId; + + public function __construct(Action $action, $groupId) + { + parent::__construct($action); + $this->groupId = $groupId; + } + + public function getGroupId() + { + return $this->groupId; + } +} + +/** @ODM\Document(collection="rdt_group_main_activity_stream_item") */ +class GroupMainActivityStreamItem extends GroupActivityStreamItem +{ +} + +/** @ODM\Document(collection="rdt_group_members_activity_stream_item") */ +class GroupMembersActivityStreamItem extends GroupActivityStreamItem +{ +} + +/** + * @ODM\MappedSuperclass + * @ODM\UniqueIndex(keys={"userId"="asc", "action.$id"="asc"}, options={"unique"="true", "dropDups"="true"}) + */ +abstract class UserActivityStreamItem extends ActivityStreamItem +{ + /** @ODM\String */ + protected $userId; + + public function __construct(Action $action, $userId) + { + parent::__construct($action); + $this->userId = $userId; + } + + public function getUserId() + { + return $this->userId; + } +} + +/** @ODM\Document(collection="rdt_user_dashboard_activity_stream_item") */ +class UserDashboardActivityStreamItem extends UserActivityStreamItem +{ +} + +/** @ODM\Document(collection="rdt_user_profile_activity_stream_item") */ +class UserProfileActivityStreamItem extends UserActivityStreamItem +{ +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceEmbeddedDocumentsTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceEmbeddedDocumentsTest.php new file mode 100644 index 00000000..9b4fd1e3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceEmbeddedDocumentsTest.php @@ -0,0 +1,66 @@ +dm->persist($project); + $this->dm->flush(); + $this->dm->clear(); + + $project = $this->dm->find('Documents\Project', $project->getId()); + + $subProjects = new ArrayCollection(); + $subProject1 = new SubProject('Sub Project #1'); + $subProject2 = new SubProject('Sub Project #2'); + + $subProject1->setIssues(new ArrayCollection(array( + new Issue('Issue #1', 'Issue #1 on Sub Project #1'), + new Issue('Issue #2', 'Issue #2 on Sub Project #1') + ))); + + $subProject2->setIssues(new ArrayCollection(array( + new Issue('Issue #1', 'Issue #1 on Sub Project #2'), + new Issue('Issue #2', 'Issue #2 on Sub Project #2') + ))); + + $subProjects->add($subProject1); + $subProjects->add($subProject2); + + $project->setSubProjects($subProjects); + + $this->dm->flush(); + $this->dm->clear(); + + $project = $this->dm->find('Documents\Project', $project->getId()); + + $subProjects = $project->getSubProjects(); + + $this->assertEquals(2, $subProjects->count()); + + $this->assertFirstSubProject($subProjects->first()); + $this->assertLastSubProject($subProjects->last()); + } + + private function assertFirstSubProject(SubProject $project) + { + $this->assertEquals('Sub Project #1', $project->getName()); + $this->assertEquals(2, $project->getIssues()->count()); + } + + private function assertLastSubProject(SubProject $project) + { + $this->assertEquals('Sub Project #2', $project->getName()); + $this->assertEquals(2, $project->getIssues()->count()); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceRepositoryMethodTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceRepositoryMethodTest.php new file mode 100644 index 00000000..1d90271c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferenceRepositoryMethodTest.php @@ -0,0 +1,70 @@ +setTimestamp(strtotime('-20 seconds')); + + $date2 = new \DateTime(); + $date2->setTimestamp(strtotime('-10 seconds')); + + $blogPost = new \Documents\BlogPost('Test'); + $blogPost->addComment(new \Documents\Comment('Comment 1', $date1)); + $blogPost->addComment(new \Documents\Comment('Comment 2', $date2)); + $this->dm->persist($blogPost); + $this->dm->flush(); + $this->dm->clear(); + + $blogPost = $this->dm->createQueryBuilder('Documents\BlogPost') + ->getQuery() + ->getSingleResult(); + + $this->assertEquals('Comment 2', $blogPost->repoComment->getText()); + $this->assertEquals('Comment 1', $blogPost->repoComments[0]->getText()); + $this->assertEquals('Comment 2', $blogPost->repoComments[1]->getText()); + } + + /** + * Tests Bi-Directional Reference "one to many" with nullable=true flag + * + * @url http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/bidirectional-references.html + */ + public function testOneToMany() + { + $user = new User(); + $user->setUsername('w00ting'); + $this->dm->persist($user); + $this->dm->flush(); + + $post1 = new BlogPost(); + $post1->name = 'post1'; + $post1->setUser($user); + + $post2 = new BlogPost(); + $post2->name = 'post2'; + $post2->setUser($user); + + $post3 = new BlogPost(); + $post3->name = 'post3'; + $post3->setUser($user); + + $this->dm->persist($post1); + $this->dm->persist($post2); + $this->dm->persist($post3); + $this->dm->flush(); + + $post1->setUser(null); + $this->dm->flush(); + + $this->assertNull($post1->user); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php new file mode 100644 index 00000000..86afb955 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -0,0 +1,216 @@ +addGroup(new Group('Group 1')); + $user->addGroup(new Group('Group 2')); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id') + ->equals($user->getId()); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + + $this->dm->remove($user2); + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder('Documents\Group'); + $query = $qb->getQuery(); + $groups = $query->execute(); + + $count = $groups->count(); + + $this->assertEquals(0, $count); + } + + public function testLazyLoadReference() + { + $user = new User(); + $profile = new Profile(); + $profile->setFirstName('Jonathan'); + $profile->setLastName('Wage'); + $user->setProfile($profile); + $user->setUsername('jwage'); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + $query = $qb->getQuery(); + + $user = $query->getSingleResult(); + + $profile = $user->getProfile(); + + $this->assertTrue($profile instanceof \Proxies\__CG__\Documents\Profile); + + $profile->getFirstName(); + + $this->assertEquals('Jonathan', $profile->getFirstName()); + $this->assertEquals('Wage', $profile->getLastName()); + } + + public function testOneEmbedded() + { + $address = new Address(); + $address->setAddress('6512 Mercomatic Ct.'); + $address->setCity('Nashville'); + $address->setState('TN'); + $address->setZipcode('37209'); + + $user = new User(); + $user->setUsername('jwage'); + + $this->dm->persist($user); + $this->dm->flush(); + + $user->setAddress($address); + + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + $this->assertEquals($user->getAddress(), $user2->getAddress()); + } + + public function testManyEmbedded() + { + $user = new \Documents\User(); + $user->addPhonenumber(new Phonenumber('6155139185')); + $user->addPhonenumber(new Phonenumber('6153303769')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + $this->assertEquals($user->getPhonenumbers()->toArray(), $user2->getPhonenumbers()->toArray()); + } + + public function testOneReference() + { + $account = new Account(); + $account->setName('Test Account'); + + $user = new User(); + $user->setUsername('jwage'); + $user->setAccount($account); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->dm->flush(); + $this->dm->clear(); + + $accountId = $user->getAccount()->getId(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + } + + public function testManyReference() + { + $user = new \Documents\User(); + $user->addGroup(new Group('Group 1')); + $user->addGroup(new Group('Group 2')); + + $this->dm->persist($user); + $this->dm->flush(); + + $groups = $user->getGroups(); + + $this->assertTrue($groups instanceof PersistentCollection); + $this->assertTrue($groups[0]->getId() !== ''); + $this->assertTrue($groups[1]->getId() !== ''); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id') + ->equals($user->getId()); + $query = $qb->getQuery(); + $user2 = $query->getSingleResult(); + $groups = $user2->getGroups(); + $this->assertFalse($groups->isInitialized()); + + $groups->count(); + $this->assertFalse($groups->isInitialized()); + + $groups->isEmpty(); + $this->assertFalse($groups->isInitialized()); + + $groups = $user2->getGroups(); + + $this->assertTrue($groups instanceof PersistentCollection); + $this->assertTrue($groups[0] instanceof Group); + $this->assertTrue($groups[1] instanceof Group); + + $this->assertTrue($groups->isInitialized()); + + unset($groups[0]); + $groups[1]->setName('test'); + + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('id')->equals($user->getId()); + $query = $qb->getQuery(); + $user3 = $query->getSingleResult(); + $groups = $user3->getGroups(); + + $this->assertEquals('test', $groups[0]->getName()); + $this->assertEquals(1, count($groups)); + } + + public function testSortReferenceManyOwningSide() + { + $user = new \Documents\User(); + $user->addGroup(new Group('Group 1')); + $user->addGroup(new Group('Group 2')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find(get_class($user), $user->getId()); + + $groups = $user->getSortedAscGroups(); + $this->assertEquals(2, $groups->count()); + $this->assertEquals('Group 1', $groups[0]->getName()); + $this->assertEquals('Group 2', $groups[1]->getName()); + + $groups = $user->getSortedDescGroups(); + $this->assertEquals(2, $groups->count()); + $this->assertEquals('Group 2', $groups[0]->getName()); + $this->assertEquals('Group 1', $groups[1]->getName()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RemoveTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RemoveTest.php new file mode 100644 index 00000000..483e3c5b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RemoveTest.php @@ -0,0 +1,64 @@ +setName('Jon Test Account'); + + $user = new User(); + $user->setUsername('jon'); + $user->setPassword('changeme'); + $user->setAccount($account); + + $this->dm->persist($user); + $this->dm->flush(); + + $this->dm->remove($user); + $this->dm->flush(); + + $account = $this->dm->find('Documents\Account', $account->getId()); + $this->assertNull($account); + + $user = $this->dm->find('Documents\User', $user->getId()); + $this->assertNull($user); + } + + + public function testUnsetFromEmbeddedCollection() + { + $user = new User(); + $user->setUsername('jon'); + $user->addGroup(new Group('test group 1')); + $user->addGroup(new Group('test group 2')); + $user->addGroup(new Group('test group 3')); + + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find('Documents\User', $user->getId()); + + $groups = $user->getGroups(); + unset($groups[0]); + $this->assertEquals(2, count($user->getGroups())); + + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jon')); + + $this->assertEquals(2, count($user->getGroups())); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RepositoriesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RepositoriesTest.php new file mode 100644 index 00000000..35ce2bad --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RepositoriesTest.php @@ -0,0 +1,50 @@ +user = new User(); + $this->user->setUsername('w00ting'); + + $this->dm->persist($this->user); + $this->dm->flush(); + + $this->repository = $this->dm->getRepository('Documents\User'); + } + + public function testMagicMethods() + { + $user = $this->repository->findOneByUsername('w00ting'); + $this->assertEquals('w00ting', $user->getUsername()); + } + + public function testFindAll() + { + $users = $this->repository->findAll(); + + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Cursor', $users); + $this->assertEquals(1, count($users)); + } + + public function testFind() + { + $user2 = $this->repository->find($this->user->getId()); + $this->assertTrue($this->user === $user2); + + $user3 = $this->repository->findOneBy(array('username' => 'w00ting')); + $this->assertTrue($user2 === $user3); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RequireIndexesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RequireIndexesTest.php new file mode 100644 index 00000000..a77d771a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/RequireIndexesTest.php @@ -0,0 +1,315 @@ +dm->getSchemaManager()->ensureDocumentIndexes('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + } + + public function testGetFieldsInQueryWithSimpleEquals() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('test')->equals('test'); + $query = $qb->getQuery(); + $this->assertEquals(array('test'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryIgnoresWhereOperator() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->where('this.test > 0'); + $qb->addOr($qb->expr()->where('this.ok > 1')); + $qb->addAnd($qb->expr()->field('username')->equals('jwage')); + $query = $qb->getQuery(); + $this->assertEquals(array('username'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithElemMatch() + { + $date = new \DateTime(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('flashes')->elemMatch( + $qb->expr()->field('startDate')->lt($date)->field('endDate')->gte($date) + ); + $query = $qb->getQuery(); + $this->assertEquals(array( + 'flashes.startDate', + 'flashes.endDate' + ), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithElemMatchAndOr() + { + $date = new \DateTime(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('flashes')->elemMatch( + $qb->expr()->field('startDate')->lt($date)->field('endDate')->gte($date)->field('startDate') + ->addOr($qb->expr()->field('something')->equals($date)) + + ->where('this.id > 0') + )->addAnd($qb->expr()->field('flashes.id')->equals('foo')); + $query = $qb->getQuery(); + $this->assertEquals(array( + 'flashes.startDate', + 'flashes.endDate', + 'flashes.something', + 'flashes.id' + ), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithOrAndIn() + { + $date = new \DateTime(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->addOr($qb->expr()->field('field1')->in(array(1))); + $qb->addOr($qb->expr()->field('field2')->in(array(1))); + $query = $qb->getQuery(); + $this->assertEquals(array('field1', 'field2'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithComplexQuery() + { + $date = new \DateTime(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->addOr($qb->expr()->field('field1')->in(array(1))); + $qb->addAnd($qb->expr()->field('field2')->equals(1)); + $qb->field('field3')->elemMatch($qb->expr()->field('embedded')->range(1, 2)); + $qb->field('field4')->elemMatch($qb->expr()->addOr($qb->expr()->field('embedded')->equals($date))); + $qb->field('field5')->equals('test'); + $query = $qb->getQuery(); + $this->assertEquals(array( + 'field1', + 'field2', + 'field3.embedded', + 'field4.embedded', + 'field5' + ), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithIn() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('test')->in(array(1)); + $query = $qb->getQuery(); + $this->assertEquals(array('test'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithNotIn() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('test')->notIn(array(1)); + $query = $qb->getQuery(); + $this->assertEquals(array('test'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithNotEqual() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('test')->notEqual(1); + $query = $qb->getQuery(); + $this->assertEquals(array('test'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithNot() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('test')->not(1); + $query = $qb->getQuery(); + $this->assertEquals(array('test'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithReferences() + { + $reference = new DoesNotRequireIndexesDocument(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('reference')->references($reference); + $qb->field('simpleReference')->references($reference); + $query = $qb->getQuery(); + $this->assertEquals(array('reference.$id', 'simpleReference'), $query->getFieldsInQuery()); + } + + public function testGetFieldsInQueryWithIncludesReferences() + { + $reference = new DoesNotRequireIndexesDocument(); + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument'); + $qb->field('reference')->includesReferenceTo($reference); + $qb->field('simpleReference')->includesReferenceTo($reference); + $query = $qb->getQuery(); + $this->assertEquals(array('reference.$id', 'simpleReference'), $query->getFieldsInQuery()); + } + + public function testIsIndexedTrue() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('indexed')->equals('test'); + $query = $qb->getQuery(); + $this->assertTrue($query->isIndexed()); + } + + public function testIsIndexedFalse() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('notIndexed')->equals('test'); + $query = $qb->getQuery(); + $this->assertFalse($query->isIndexed()); + + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('indexed')->equals('test') + ->field('notIndexed')->equals('test'); + $query = $qb->getQuery(); + $this->assertFalse($query->isIndexed()); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\MongoDBException + */ + public function testRequireIndexesThrowsExceptionOnExecute() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('notIndexed')->equals('test'); + $query = $qb->getQuery(); + $query->execute(); + } + + public function testRequireIndexesExceptionMessage() + { + try { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('notIndexed')->equals('test'); + $query = $qb->getQuery(); + $query->execute(); + } catch (MongoDBException $e) { + $this->assertEquals('Cannot execute unindexed queries on Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument. Unindexed fields: notIndexed', $e->getMessage()); + } + } + + /** + * @expectedException Doctrine\ODM\MongoDB\MongoDBException + */ + public function testForceEnableRequireIndexes() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\DoesNotRequireIndexesDocument') + ->field('notIndexed')->equals('test') + ->requireIndexes(); + $query = $qb->getQuery(); + $query->execute(); + } + + public function testForceDisableRequireIndexes() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('notIndexed')->equals('test') + ->requireIndexes(false); + $query = $qb->getQuery(); + $query->execute(); + } + + + public function testRequireIndexesFalse() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\DoesNotRequireIndexesDocument') + ->field('notIndexed')->equals('test'); + $query = $qb->getQuery(); + $query->execute(); + } + + public function testRequireIndexesOnEmbeddedDocument() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('embedOne.indexed')->equals('test'); + $query = $qb->getQuery(); + $query->execute(); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\MongoDBException + */ + public function testRequireIndexesOnEmbeddedDocumentThrowsException() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('embedOne.notIndexed')->equals('test'); + $query = $qb->getQuery(); + $query->execute(); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\MongoDBException + */ + public function testRequireIndexesOnSortThrowException() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->sort('embedOne.notIndexed', 'asc'); + $query = $qb->getQuery(); + $query->execute(); + } + + public function testGetUnindexedFields() + { + $qb = $this->dm->createQueryBuilder('Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesDocument') + ->field('embedOne.notIndexed')->equals('test') + ->field('notIndexed')->equals('test') + ->field('indexed')->equals('test'); + $query = $qb->getQuery(); + $this->assertEquals(array('embedOne.notIndexed', 'notIndexed'), $query->getUnindexedFields()); + } +} + +/** + * @ODM\Document(requireIndexes=true) + */ +class RequireIndexesDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String @ODM\Index */ + public $indexed; + + /** @ODM\String */ + public $notIndexed; + + /** @ODM\EmbedOne(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesEmbeddedDocument") */ + public $embedOne; + + /** @ODM\EmbedMany(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\RequireIndexesEmbeddedDocument") */ + public $embedMany; + + /** @ODM\ReferenceOne(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\DoesNotRequireIndexesDocument") */ + public $reference; + + /** @ODM\ReferenceOne(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\DoesNotRequireIndexesDocument", simple=true) */ + public $simpleReference; +} + +/** + * @ODM\Document(requireIndexes=false) + */ +class DoesNotRequireIndexesDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String @ODM\Index */ + public $indexed; + + /** @ODM\String */ + public $notIndexed; +} + +/** @ODM\EmbeddedDocument */ +class RequireIndexesEmbeddedDocument +{ + /** @ODM\String @ODM\Index */ + public $indexed; + + /** @ODM\String */ + public $notIndexed; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php new file mode 100644 index 00000000..a34bb63f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php @@ -0,0 +1,118 @@ +user = new User(); + $this->user->setUsername('jwage'); + $this->test = new SimpleReferenceUser(); + $this->test->setName('test'); + $this->test->setUser($this->user); + $this->test->addUser($this->user); + $this->test->addUser($this->user); + $this->dm->persist($this->test); + $this->dm->persist($this->user); + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + } + + public function testIndexes() + { + $indexes = $this->dm->getSchemaManager()->getDocumentIndexes('Documents\SimpleReferenceUser'); + $this->assertEquals(array('userId' => 1), $indexes[0]['keys']); + } + + public function testStorage() + { + $test = $this->dm->getDocumentCollection('Documents\SimpleReferenceUser')->findOne(); + $this->assertNotNull($test); + $this->assertInstanceOf('MongoId', $test['userId']); + $this->assertInstanceOf('MongoId', $test['users'][0]); + } + + public function testQuery() + { + $this->user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jwage')); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->references($this->user); + $this->assertEquals(array('userId' => new \MongoId($this->user->getId())), $qb->getQuery()->debug()); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->equals($this->user->getId()); + $this->assertEquals(array('userId' => new \MongoId($this->user->getId())), $qb->getQuery()->debug()); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->in(array($this->user->getId())); + $this->assertEquals(array('userId' => array('$in' => array(new \MongoId($this->user->getId())))), $qb->getQuery()->debug()); + } + + public function testProxy() + { + $this->user = $this->dm->getRepository('Documents\User')->findOneBy(array('username' => 'jwage')); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->references($this->user); + $this->assertEquals(array('userId' => new \MongoId($this->user->getId())), $qb->getQuery()->debug()); + + $this->dm->clear(); + + $test = $qb->getQuery()->getSingleResult(); + + $this->assertNotNull($test); + $this->assertNotNull($test->getUser()); + $this->assertInstanceOf('Proxies\__CG__\Documents\User', $test->getUser()); + $this->assertFalse($test->getUser()->__isInitialized__); + $this->assertEquals('jwage', $test->getUser()->getUsername()); + $this->assertTrue($test->getUser()->__isInitialized__); + } + + public function testPersistentCollectionOwningSide() + { + $test = $this->dm->getRepository('Documents\SimpleReferenceUser')->findOneBy(array()); + $users = $test->getUsers()->toArray(); + $this->assertEquals(2, $test->getUsers()->count()); + $this->assertEquals('jwage', current($users)->getUsername()); + $this->assertEquals('jwage', end($users)->getUsername()); + } + + public function testPersistentCollectionInverseSide() + { + $user = $this->dm->getRepository('Documents\User')->findOneBy(array()); + $test = $user->getSimpleReferenceManyInverse()->toArray(); + $this->assertEquals('test' ,current($test)->getName()); + } + + public function testOneInverseSide() + { + $user = $this->dm->getRepository('Documents\User')->findOneBy(array()); + $test = $user->getSimpleReferenceOneInverse(); + $this->assertEquals('test', $test->getName()); + } + + public function testQueryForNonIds() { + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->equals(null); + $this->assertEquals(array('userId' => null), $qb->getQueryArray()); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->notEqual(null); + $this->assertEquals(array('userId' => array('$ne' => null)), $qb->getQueryArray()); + + $qb = $this->dm->createQueryBuilder('Documents\SimpleReferenceUser'); + $qb->field('user')->exists(true); + $this->assertEquals(array('userId' => array('$exists' => true)), $qb->getQueryArray()); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleTest.php new file mode 100644 index 00000000..ca4e0126 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleTest.php @@ -0,0 +1,30 @@ +addLocation(new Location('West Nashville')); + $bar->addLocation(new Location('East Nashville')); + $bar->addLocation(new Location('North Nashville')); + $this->dm->persist($bar); + $this->dm->flush(); + $this->dm->clear(); + + $bar = $this->dm->find('Documents\Bars\Bar', $bar->getId()); + + $locations = $bar->getLocations(); + unset($locations[0]); + + $this->dm->flush(); + + $test = $this->dm->getDocumentCollection('Documents\Bars\Bar')->findOne(); + $this->assertEquals(2, count($test['locations'])); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SlaveOkayTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SlaveOkayTest.php new file mode 100644 index 00000000..fae8c710 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/SlaveOkayTest.php @@ -0,0 +1,93 @@ +addGroup(new Group('Test')); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User') + ->createQueryBuilder() + ->slaveOkay(false) + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(array(), $user->getGroups()->getHints()); + + $this->dm->clear(); + + $users = $this->dm->getRepository('Documents\User') + ->createQueryBuilder() + ->getQuery() + ->execute(); + + $this->assertEquals(array(), $users->getHints()); + + $users = array_values($users->toArray()); + $user = $users[0]; + $this->assertEquals(array(), $user->getGroups()->getHints()); + + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User') + ->createQueryBuilder() + ->slaveOkay(true) + ->getQuery() + ->getSingleResult(); + + $this->assertEquals(array(Query::HINT_SLAVE_OKAY => true), $user->getGroups()->getHints()); + + $this->dm->clear(); + + $users = $this->dm->getRepository('Documents\User') + ->createQueryBuilder() + ->getQuery() + ->execute() + ->slaveOkay(true); + + $this->assertEquals(array(Query::HINT_SLAVE_OKAY => true), $users->getHints()); + + $users = array_values($users->toArray()); + $user = $users[0]; + $this->assertEquals(array(Query::HINT_SLAVE_OKAY => true), $user->getGroups()->getHints()); + + $this->dm->clear(); + + $user = $this->dm->getRepository('Documents\User') + ->createQueryBuilder() + ->getQuery() + ->getSingleResult(); + + $groups = $user->getGroups(); + $groups->setHints(array(Query::HINT_SLAVE_OKAY => true)); + $this->assertEquals(array(Query::HINT_SLAVE_OKAY => true), $groups->getHints()); + } + + public function testSlaveOkayDocument() + { + $users = $this->dm->getRepository(__NAMESPACE__.'\SlaveOkayDocument') + ->createQueryBuilder() + ->getQuery() + ->execute(); + + $this->assertEquals(array(Query::HINT_SLAVE_OKAY => true), $users->getHints()); + } +} + +/** @ODM\Document(slaveOkay=true) */ +class SlaveOkayDocument +{ + /** @ODM\Id */ + public $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php new file mode 100644 index 00000000..7bd43c89 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php @@ -0,0 +1,34 @@ +reference = new TargetDocumentTestReference(); + $this->dm->persist($test); + $this->dm->persist($test->reference); + $this->dm->flush(); + } +} + +/** @ODM\Document */ +class TargetDocumentTestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\ReferenceOne(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\TargetDocumentTestReference") */ + public $reference; +} + +/** @ODM\MappedSuperclass */ +abstract class TargetDocumentTestReference +{ + /** @ODM\Id */ + public $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH232Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH232Test.php new file mode 100644 index 00000000..efa6cfe8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH232Test.php @@ -0,0 +1,80 @@ + + * @since 6/26/12 + */ +class GH232Test extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + public function testReferencedDocumentInsideEmbeddedDocument() + { + /* PARENT DOCUMENT */ + $product = new Product('Product'); + /* END PARENT DOCUMENT */ + + /* ADD EMBEDDED DOCUMENT */ + $sub_product = new SubProduct(); + $product->subproducts->add($sub_product); + + $price = new Price(); + $sub_product->prices->add($price); + /* END ADD EMBEDDED DOCUMENT */ + + // persist & double flush + $this->dm->persist($product); + $this->dm->flush(); + $this->dm->flush(); + $this->dm->clear(); + + $product = $this->dm->getRepository(__NAMESPACE__ . '\Product')->findOneByName('Product'); + + $this->assertEquals(1, $product->subproducts->count()); + $this->assertEquals(1, $product->subproducts[0]->prices->count()); + } +} + +/** @ODM\Document */ +class Product +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="Price") */ + public $prices = array(); + + /** @ODM\EmbedMany(targetDocument="SubProduct") */ + public $subproducts = array(); + + public function __construct($name) + { + $this->name = $name; + $this->subproducts = new ArrayCollection(); + } +} + +/** @ODM\EmbeddedDocument */ +class SubProduct +{ + /** @ODM\EmbedMany(targetDocument="Price") */ + public $prices = array(); + + public function __construct() + { + $this->prices = new ArrayCollection(); + } +} + +/** @ODM\EmbeddedDocument */ +class Price +{ + /** @ODM\String */ + public $price; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php new file mode 100644 index 00000000..e76e7218 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php @@ -0,0 +1,46 @@ +id = 1; + + $orderLog = new GH245OrderLog(); + $orderLog->order = $order; + + $this->dm->persist($orderLog); + $this->dm->persist($order); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->find(get_class($order), $order->id); + + $this->assertTrue(is_int($order->id)); + + $check = $this->dm->getDocumentCollection(get_class($orderLog))->findOne(); + $this->assertTrue(is_int($check['order']['$id'])); + } +} + +/** @ODM\Document */ +class GH245Order +{ + /** @ODM\Id(strategy="NONE") */ + public $id; +} + +/** @ODM\Document */ +class GH245OrderLog +{ + /** @ODM\Id */ + public $id; + + /** @ODM\ReferenceOne(targetDocument="GH245Order") */ + public $order; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH267Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH267Test.php new file mode 100644 index 00000000..63949299 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH267Test.php @@ -0,0 +1,158 @@ +setCompany($company); + $user2->setCompany($company); + $user3->setCompany($company); + + $this->dm->persist($company); + $this->dm->flush(); + + $this->dm->persist($user1); + $this->dm->persist($user2); + $this->dm->persist($user3); + $this->dm->flush(); + + // Get ids for use later + $user1Id = $user1->getId(); + $companyId = $company->getId(); + + // Clear out DM and read from DB afresh + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__ . '\User') + ->field('_id')->equals($user1Id); + + $query = $qb->getQuery(); + $dbUser = $query->execute()->getNext(); + + // Assert user name + $this->assertEquals('Tom Petty', $dbUser->getName()); + + // Assert company id + $this->assertEquals($companyId, $dbUser->getCompany()->getId()); + + // Assert number of users + $this->assertEquals(3, $dbUser->getCompany()->getUsers()->count(true)); + } +} + +/** + * @ODM\Document(collection="users") + */ +class User +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $name; + + /** + * @ODM\ReferenceOne(name="company", targetDocument="Company", discriminatorMap={"seller"="SellerCompany", "buyer"="BuyerCompany"}, inversedBy="users") + */ + protected $company; + + public function __construct($name) + { + $this->name = $name; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setCompany($company) + { + $this->company = $company; + } + + public function getCompany() + { + return $this->company; + } +} + +/** + * @ODM\Document(collection="companies") + * @ODM\InheritanceType("SINGLE_COLLECTION") + * @ODM\DiscriminatorField(fieldName="type") + * @ODM\DiscriminatorMap({"seller"="SellerCompany", "buyer"="BuyerCompany"}) + */ +class Company +{ + /** @ODM\Id */ + protected $id; + + /** + * @ODM\ReferenceMany(targetDocument="User", mappedBy="company") + */ + protected $users; + + public function setId($id) + { + $this->id = $id; + } + + public function getId() + { + return $this->id; + } + + public function setUsers($users) + { + $this->users = $users; + } + + public function getUsers() + { + return $this->users; + } +} + +/** + * @ODM\Document(collection="companies") + */ +class BuyerCompany extends Company +{ + +} + +/** + * @ODM\Document(collection="companies") + */ +class SellerCompany extends Company +{ + +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH385Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH385Test.php new file mode 100644 index 00000000..56351089 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH385Test.php @@ -0,0 +1,30 @@ +dm->createQueryBuilder('Documents\User') + ->upsert() + ->update() + ->field('id')->equals(1) + ->field('foo.bar.level3a')->inc(1) + ->field('foo.bar.level3b')->inc(1); + + $debug = $qb->getQuery()->getQuery(); + $this->assertEquals(array('$inc' => array('foo.bar.level3a' => 1, 'foo.bar.level3b' => 1)), $debug['newObj']); + + $qb->getQuery()->execute(); + + $check = $this->dm->getDocumentCollection('Documents\User')->findOne(); + $this->assertNotNull($check); + $this->assertTrue(isset($check['foo']['bar']['level3a'])); + $this->assertTrue(isset($check['foo']['bar']['level3b'])); + $this->assertEquals(1, $check['foo']['bar']['level3a']); + $this->assertEquals(1, $check['foo']['bar']['level3b']); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH389Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH389Test.php new file mode 100644 index 00000000..3cff3e84 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH389Test.php @@ -0,0 +1,73 @@ + + */ +class GH389Test extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + public function testDiscriminatorEmptyEmbeddedDocument() + { + //Create root document (with empty embedded document) + $rootDocument = new RootDocument(); + + //Persist root document + $this->dm->persist($rootDocument); + $this->dm->flush(); + $this->dm->clear(); + + //Get root document id + $rootDocumentId = $rootDocument->getId(); + unset($rootDocument); + + //Get root document + $rootDocument = $this->dm->getRepository(__NAMESPACE__ . '\RootDocument')->find($rootDocumentId); + + //Test + $this->assertInstanceOf(__NAMESPACE__ . '\EmptyEmbeddedDocument', $rootDocument->getEmptyEmbeddedDocument()); + } +} + +/** @ODM\Document */ +class RootDocument +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\EmbedOne(targetDocument="EmptyMappedSuperClass") */ + protected $emptyEmbeddedDocument; + + public function __construct() + { + $this->emptyEmbeddedDocument = new EmptyEmbeddedDocument(); + } + + public function getId() + { + return $this->id; + } + + public function getEmptyEmbeddedDocument() + { + return $this->emptyEmbeddedDocument; + } +} + +/** + * @ODM\MappedSuperClass + * @ODM\DiscriminatorField(fieldName="foobar") + * @ODM\DiscriminatorMap({ + * "empty"="EmptyEmbeddedDocument" + * }) + */ +class EmptyMappedSuperClass +{ +} + +/** @ODM\EmbeddedDocument */ +class EmptyEmbeddedDocument extends EmptyMappedSuperClass +{ +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH426Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH426Test.php new file mode 100644 index 00000000..1f8545a8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH426Test.php @@ -0,0 +1,58 @@ +fields[] = new GH426Field($form); + $form->fields[] = new GH426Field($form); + + $this->dm->persist($form); + $this->dm->flush(); + $this->dm->clear(); + + $form = $this->dm->find('Doctrine\ODM\MongoDB\Tests\Functional\Ticket\GH426Form', $form->id); + + $this->assertEquals(2, $form->fields->count()); + $this->assertSame($form->fields[0], $form->firstField); + $this->assertSame($form->fields[1], $form->lastField); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Tests\Functional\Ticket\GH426Field', $form->firstField); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Tests\Functional\Ticket\GH426Field', $form->lastField); + } +} + +/** @ODM\Document */ +class GH426Form +{ + /** @ODM\Id */ + public $id; + + /** @ODM\ReferenceMany(targetDocument="GH426Field", mappedBy="form", cascade={"all"}) */ + public $fields = array(); + + /** @ODM\ReferenceOne(targetDocument="GH426Field", mappedBy="form", sort={"_id":1}) */ + public $firstField; + + /** @ODM\ReferenceOne(targetDocument="GH426Field", mappedBy="form", sort={"_id":-1}) */ + public $lastField; +} + +/** @ODM\Document */ +class GH426Field +{ + /** @ODM\Id */ + public $id; + + /** @ODM\ReferenceOne(inversedBy="fields", discriminatorMap={"f":"GH426Form"}, discriminatorField="type", cascade={"all"}) */ + public $form; + + public function __construct(GH426Form $form) + { + $this->form = $form; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH435Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH435Test.php new file mode 100644 index 00000000..6c4b0572 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH435Test.php @@ -0,0 +1,47 @@ +dm->getClassMetadata('Doctrine\ODM\MongoDB\Tests\Functional\Ticket\GH426A'); + + $aTestFieldMappings = $a->fieldMappings['test']; + $this->assertEquals('int', $aTestFieldMappings['type']); + + $aIdFieldMappings = $a->fieldMappings['id']; + $this->assertTrue(isset($aIdFieldMappings['id'])); + + $b = $this->dm->getClassMetadata('Doctrine\ODM\MongoDB\Tests\Functional\Ticket\GH426B'); + + $bTestFieldMappings = $b->fieldMappings['test']; + $this->assertEquals('string', $bTestFieldMappings['type']); + + $bIdFieldMappings = $b->fieldMappings['id']; + $this->assertFalse(isset($bIdFieldMappings['id'])); + } +} + +/** @ODM\Document */ +class GH426A +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\Int */ + protected $test; +} + +/** @ODM\Document */ +class GH426B extends GH426A +{ + /** @ODM\String */ + protected $id; + + /** @ODM\String */ + protected $test; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php new file mode 100644 index 00000000..7e9e1e7a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php @@ -0,0 +1,278 @@ + 'x', 'b' => 'y', 'c' => 'z'); + + $doc = new GH453Document(); + $doc->hash = $hash; + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); + + // Check that the value is hydrated properly + $doc = $this->dm->find(get_class($doc), $doc->id); + + $this->assertSame($hash, $doc->hash); + + $this->dm->clear(); + + // Check that the value is changed properly + unset($hash['b']); + $doc = $this->dm->merge($doc); + $doc->hash = $hash; + + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); + } + + public function testHashWithNumericKeys() + { + $hash = array(0 => 'x', 1 => 'y', 2 => 'z'); + + $doc = new GH453Document(); + $doc->hash = $hash; + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); + + // Check that the value is hydrated properly + $doc = $this->dm->find(get_class($doc), $doc->id); + + $this->assertSame($hash, $doc->hash); + + $this->dm->clear(); + + // Check that the value is changed properly + unset($hash[1]); + $doc = $this->dm->merge($doc); + $doc->hash = $hash; + + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); + } + + public function testCollection() + { + $col = array('x', 'y', 'z'); + + $doc = new GH453Document(); + $doc->colPush = $col; + $doc->colSet = $col; + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonArrayAndValue($col, $doc->id, 'colPush'); + $this->assertBsonArrayAndValue($col, $doc->id, 'colSet'); + + // Check that the value is hydrated properly + $doc = $this->dm->find(get_class($doc), $doc->id); + + $this->assertSame($col, $doc->colPush); + $this->assertSame($col, $doc->colSet); + + $this->dm->clear(); + + // Check that the value is changed properly + unset($col[1]); + $doc = $this->dm->merge($doc); + $doc->colPush = $col; + $doc->colSet = $col; + + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonArrayAndValue($col, $doc->id, 'colPush'); + $this->assertBsonArrayAndValue($col, $doc->id, 'colSet'); + } + + public function testEmbedMany() + { + $colPush = new ArrayCollection(array( + new GH453EmbeddedDocument(), + new GH453EmbeddedDocument(), + new GH453EmbeddedDocument(), + )); + $colSet = $colPush->map(function($v) { return clone $v; }); + + $doc = new GH453Document(); + $doc->embedManyPush = $colPush; + $doc->embedManySet = $colSet; + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + // No need to assert the value of the embedded document structure + $this->assertBsonArray($doc->id, 'embedManyPush'); + $this->assertBsonArray($doc->id, 'embedManySet'); + + // Check that the value is changed properly + unset($colPush[1], $colSet[1]); + $doc = $this->dm->merge($doc); + $doc->embedManyPush = $colPush; + $doc->embedManySet = $colSet; + + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonArray($doc->id, 'embedManyPush'); + $this->assertBsonArray($doc->id, 'embedManySet'); + } + + public function testReferenceMany() + { + $colPush = new ArrayCollection(array( + new GH453ReferencedDocument(), + new GH453ReferencedDocument(), + new GH453ReferencedDocument(), + )); + $colSet = $colPush->map(function($v) { return clone $v; }); + + $dm = $this->dm; + $colPush->forAll(function($k, $v) use ($dm) { $dm->persist($v); }); + $colSet->forAll(function($k, $v) use ($dm) { $dm->persist($v); }); + + $doc = new GH453Document(); + $doc->referenceManyPush = $colPush; + $doc->referenceManySet = $colSet; + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + // No need to assert the value of the referenced document structure + $this->assertBsonArray($doc->id, 'referenceManyPush'); + $this->assertBsonArray($doc->id, 'referenceManySet'); + + // Check that the value is changed properly + unset($colPush[1], $colSet[1]); + $doc = $this->dm->merge($doc); + $doc->referenceManyPush = $colPush; + $doc->referenceManySet = $colSet; + + $this->dm->flush(); + $this->dm->clear(); + + $this->assertBsonArray($doc->id, 'referenceManyPush'); + $this->assertBsonArray($doc->id, 'referenceManySet'); + } + + private function assertBsonArray($documentId, $fieldName) + { + $this->assertBsonType(4, $documentId, $fieldName); + } + + private function assertBsonObject($documentId, $fieldName) + { + $this->assertBsonType(3, $documentId, $fieldName); + } + + private function assertBsonType($bsonType, $documentId, $fieldName) + { + $criteria = array('_id' => $documentId); + + if (4 === $bsonType) { + // See: https://jira.mongodb.org/browse/SERVER-1475 + $criteria['$where'] = sprintf('Array.isArray(this.%s)', $fieldName); + } else { + $criteria[$fieldName] = array('$type' => $bsonType); + } + + $this->assertNotNull($this->dm->getRepository(__NAMESPACE__ . '\GH453Document')->findOneBy($criteria)); + } + + private function assertBsonArrayAndValue($expectedValue, $documentId, $fieldName) + { + $this->assertBsonTypeAndValue(4, $expectedValue, $documentId, $fieldName); + } + + private function assertBsonObjectAndValue($expectedValue, $documentId, $fieldName) + { + $this->assertBsonTypeAndValue(3, $expectedValue, $documentId, $fieldName); + } + + private function assertBsonTypeAndValue($bsonType, $expectedValue, $documentId, $fieldName) + { + if (4 === $bsonType) { + $expectedValue = array_values((array) $expectedValue); + } elseif (3 === $bsonType) { + $expectedValue = (object) $expectedValue; + } + + $criteria = array( + '_id' => $documentId, + '$and' => array(array($fieldName => $expectedValue)), + ); + + if (4 === $bsonType) { + // See: https://jira.mongodb.org/browse/SERVER-1475 + $criteria['$and'][] = array('$where' => sprintf('Array.isArray(this.%s)', $fieldName)); + } else { + $criteria['$and'][] = array($fieldName => array('$type' => $bsonType)); + } + + $this->assertNotNull($this->dm->getRepository(__NAMESPACE__ . '\GH453Document')->findOneBy($criteria)); + } +} + +/** @ODM\Document */ +class GH453Document +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Hash */ + public $hash; + + /** @ODM\Collection(strategy="pushAll")) */ + public $colPush; + + /** @ODM\Collection(strategy="set") */ + public $colSet; + + /** @ODM\EmbedMany(strategy="pushAll")) */ + public $embedManyPush; + + /** @ODM\EmbedMany(strategy="set") */ + public $embedManySet; + + /** @ODM\ReferenceMany(strategy="pushAll")) */ + public $referenceManyPush; + + /** @ODM\ReferenceMany(strategy="set") */ + public $referenceManySet; +} + +/** @ODM\EmbeddedDocument */ +class GH453EmbeddedDocument +{ + /** @ODM\Id */ + public $id; +} + +/** @ODM\Document */ +class GH453ReferencedDocument +{ + /** @ODM\Id */ + public $id; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH467Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH467Test.php new file mode 100644 index 00000000..e099e5bc --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH467Test.php @@ -0,0 +1,55 @@ +dm->persist($doc); + $this->dm->flush(); + $this->dm->clear(); + + $doc = $this->dm->merge($doc); + + $this->assertNull($doc->col, 'Unset basic collections are not initialized'); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\PersistentCollection', $doc->embedMany, 'Unset EmbedMany collections are initialized as empty PersistentCollections'); + $this->assertCount(0, $doc->embedMany, 'Unset EmbedMany collections are initialized as empty PersistentCollections'); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\PersistentCollection', $doc->refMany, 'Unset ReferenceMany collections are initialized as empty PersistentCollections'); + $this->assertCount(0, $doc->refMany, 'Unset ReferenceMany collections are initialized as empty PersistentCollections'); + } +} + +/** @ODM\Document */ +class GH467Document +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Collection */ + public $col; + + /** @ODM\EmbedMany(targetDocument="GH467EmbeddedDocument") */ + public $embedMany; + + /** @ODM\ReferenceMany(targetDocument="GH467EmbeddedDocument") */ + public $refMany; +} + +/** @ODM\EmbeddedDocument */ +class GH467EmbeddedDocument +{ + /** @ODM\Id */ + public $id; +} + +/** @ODM\Document */ +class GH467ReferencedDocument +{ + /** @ODM\Id */ + public $id; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php new file mode 100644 index 00000000..1beaa52e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php @@ -0,0 +1,77 @@ +setName('test'); + $parent->setChild(new MODM116Child()); + $this->dm->persist($parent->getChild()); + $this->dm->persist($parent); + $this->dm->flush(); + $this->dm->clear(); + + $parent = $this->dm->find(get_class($parent), $parent->getId()); + + $parent->getChild()->setName('ok'); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(get_class($parent))->find()->toArray(); + $check = array_values($check); + $this->assertEquals(1, count($check)); + $this->assertEquals('test', $check[0]['name']); + + $check = $this->dm->getDocumentCollection(get_class($parent->getChild()))->find()->toArray(); + $check = array_values($check); + $this->assertEquals(1, count($check)); + $this->assertEquals('ok', $check[0]['name']); + } +} + +/** @ODM\Document @ODM\InheritanceType("COLLECTION_PER_CLASS") **/ +class MODM116Parent +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; + + /** @ODM\ReferenceOne(targetDocument="MODM116Child") **/ + private $child; + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getChild() + { + return $this->child; + } + + public function setChild(MODM116Child $child) + { + $this->child = $child; + } +} + +/** @ODM\Document **/ +class MODM116Child extends MODM116Parent +{ +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM117Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM117Test.php new file mode 100644 index 00000000..9274945e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM117Test.php @@ -0,0 +1,44 @@ +first_name = 'jon'; + $user->last_name = 'wage'; + $this->dm->persist($user); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection(get_class($user))->findOne(); + $this->assertEquals('jon', $check['first_name']); + $this->assertEquals('wage', $check['last_name']); + } +} + +/** @ODM\Document */ +class MODM117User +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Field(type="string") */ + public $first_name; + + /** @ODM\Field(type="string", name="last_name") */ + protected $_last_name; + + public function __get($name) + { + return $this->{'_'.$name}; + } + + public function __set($name, $value) + { + $this->{'_'.$name} = $value; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM140Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM140Test.php new file mode 100644 index 00000000..5dab936d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM140Test.php @@ -0,0 +1,181 @@ +name = "My Category"; + + $post1 = new Post; + $post1->versions->add(new PostVersion('P1V1')); + $post1->versions->add(new PostVersion('P1V2')); + + $category->posts->add($post1); + + $this->dm->persist($category); + $this->dm->flush(); + $this->dm->clear(); + + $category = $this->dm->getRepository(__NAMESPACE__ . '\Category')->findOneByName('My Category'); + $post2 = new Post; + $post2->versions->add(new PostVersion('P2V1')); + $post2->versions->add(new PostVersion('P2V2')); + $category->posts->add($post2); + + $this->dm->flush(); + $this->dm->clear(); + + $category = $this->dm->getRepository(__NAMESPACE__ . '\Category')->findOneByName('My Category'); + // Should be: 1 Category, 2 Post, 2 PostVersion in each Post + $this->assertEquals(2, $category->posts->count()); + $this->assertEquals(2, $category->posts->get(0)->versions->count()); + $this->assertEquals(2, $category->posts->get(1)->versions->count()); + } + + public function testInsertingEmbeddedCollectionWithRefMany() + { + $comment = new Comment(); + + $post = new Post(); + $post->comments[] = $comment; + + $category = new Category(); + $category->name = "My Category"; + $category->posts->add($post); + + $this->dm->persist($category); + $this->dm->flush(); + $this->dm->clear(); + + $category = $this->dm->getRepository(__NAMESPACE__ . '\Category')->findOneByName('My Category'); + $this->assertEquals(1, $category->posts->count()); + $this->assertEquals(1, $category->posts->get(0)->comments->count()); + } + + public function testAddingAnotherEmbeddedDocument() + { + $test = new EmbeddedTestLevel0(); + $test->name = 'test'; + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository('Documents\Functional\EmbeddedTestLevel0')->findOneBy(array('name' => 'test')); + $this->assertInstanceOf('Documents\Functional\EmbeddedTestLevel0', $test); + + $level1 = new EmbeddedTestLevel1(); + $level1->name = "test level 1 #1"; + + $level2 = new EmbeddedTestLevel2(); + $level2->name = "test level 2 #1 in level 1 #1"; + $level1->level2[] = $level2; + + $level2 = new EmbeddedTestLevel2(); + $level2->name = "test level 2 #2 in level 1 #1"; + $level1->level2[] = $level2; + + $test->level1[] = $level1; + + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository('Documents\Functional\EmbeddedTestLevel0')->findOneBy(array('name' => 'test')); + $this->assertEquals(1, count($test->level1)); + $this->assertEquals(2, count($test->level1[0]->level2)); + + $level1 = new EmbeddedTestLevel1(); + $level1->name = "test level 1 #2"; + + $level2 = new EmbeddedTestLevel2(); + $level2->name = "test level 2 #1 in level 1 #2"; + $level1->level2[] = $level2; + + $level2 = new EmbeddedTestLevel2(); + $level2->name = "test level 2 #2 in level 1 #2"; + $level1->level2[] = $level2; + + $test->level1[] = $level1; + + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->getRepository('Documents\Functional\EmbeddedTestLevel0')->findOneBy(array('name' => 'test')); + $this->assertEquals(2, count($test->level1)); + $this->assertEquals(2, count($test->level1[0]->level2)); + $this->assertEquals(2, count($test->level1[1]->level2)); + } + +} + +/** @ODM\Document */ +class Category +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedMany(targetDocument="Post") */ + public $posts; + + public function __construct() + { + $this->posts = new ArrayCollection(); + } + +} + +/** @ODM\EmbeddedDocument */ +class Post +{ + + /** @ODM\EmbedMany(targetDocument="PostVersion") */ + public $versions; + + /** @ODM\ReferenceMany(targetDocument="Comment") */ + public $comments; + + public function __construct() + { + $this->versions = new ArrayCollection(); + $this->comments = new ArrayCollection(); + } + +} + +/** @ODM\EmbeddedDocument */ +class PostVersion +{ + + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } + +} + +/** @ODM\Document */ +class Comment +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + public $content; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM160Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM160Test.php new file mode 100644 index 00000000..f4e34d0c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM160Test.php @@ -0,0 +1,59 @@ +name = 'embedded test'; + + $level1 = new MODM160\EmbedManyInArrayLevel1(); + $level1->name = 'test level1 #1'; + $test->level1[] = $level1; + + $level2 = new MODM160\MODM160Level2(); + $level2->name = 'test level2 #1'; + $level1->level2[] = $level2; + + $this->dm->merge($test); + } + + public function testEmbedManyInArrayCollectionMergeNew() + { + // create a test document + $test = new MODM160\EmbedManyInArrayCollectionLevel0(); + $test->name = 'embedded test'; + + $level1 = new MODM160\EmbedManyInArrayCollectionLevel1(); + $level1->name = 'test level1 #1'; + $test->level1[] = $level1; + + $level2 = new MODM160\MODM160Level2(); + $level2->name = 'test level2 #1'; + $level1->level2[] = $level2; + + $this->dm->merge($test); + } + + public function testEmbedOneMergeNew() + { + // create a test document + $test = new MODM160\EmbedOneLevel0(); + $test->name = 'embedded test'; + + $level1 = new MODM160\EmbedOneLevel1(); + $level1->name = 'test level1 #1'; + $test->level1 = $level1; + + $level2 = new MODM160\MODM160Level2(); + $level2->name = 'test level2 #1'; + $level1->level2 = $level2; + + $this->dm->merge($test); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php new file mode 100644 index 00000000..c06d3cbf --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php @@ -0,0 +1,63 @@ +listener = new MODM166EventListener(); + $evm = $this->dm->getEventManager(); + $evm->addEventListener(Events::onFlush, $this->listener); + return $this->dm; + } + + public function testUpdateCollectionDuringOnFlushAndRecomputSingleDocumentChangeSet() + { + // create a test document + $test = new User(); + $test->setUsername('toby'); + $test->addPhonenumber(new Phonenumber('1111')); + + $this->dm->persist($test); + $this->dm->flush(); + + $test->setUsername('lucy'); + $this->dm->flush(); + $this->dm->clear(); + + $repository = $this->dm->getRepository(get_class($test)); + $test = $repository->findOneBy(array('username' => 'lucy')); + + $phonenumbers = array(); + foreach ($test->getPhonenumbers() as $phonenumber){ + $phonenumbers[] = $phonenumber->getPhonenumber(); + } + sort($phonenumbers); + + $this->assertEquals(array('1111', '2222'), $phonenumbers); + } +} + +class MODM166EventListener +{ + public function onFlush(OnFlushEventArgs $eventArgs) + { + $documentManager = $eventArgs->getDocumentManager(); + $unitOfWork = $documentManager->getUnitOfWork(); + + foreach ($unitOfWork->getScheduledDocumentUpdates() as $document) { + $metadata = $documentManager->getClassMetadata(get_class($document)); + $document->addPhonenumber(new Phonenumber('2222')); + $unitOfWork->recomputeSingleDocumentChangeSet($metadata, $document); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php new file mode 100644 index 00000000..ff6ed6fb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php @@ -0,0 +1,50 @@ +listener = new MODM167EventListener(); + $evm = $this->dm->getEventManager(); + $evm->addEventListener(Events::onFlush, $this->listener); + return $this->dm; + } + + public function testDetatchNewDocumentDuringOnFlush() + { + // create a test document + $test = new User(); + $test->setUsername('toby'); + + $this->dm->persist($test); + $this->dm->flush(); + $this->dm->clear(); + + $repository = $this->dm->getRepository(get_class($test)); + $test = $repository->find($test->getId()); + + $this->assertNull($test); + } +} + +class MODM167EventListener +{ + public function onFlush(OnFlushEventArgs $eventArgs) + { + $documentManager = $eventArgs->getDocumentManager(); + $unitOfWork = $documentManager->getUnitOfWork(); + + foreach ($unitOfWork->getScheduledDocumentInsertions() as $document) { + $unitOfWork->detach($document); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM29Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM29Test.php new file mode 100644 index 00000000..135b32c0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM29Test.php @@ -0,0 +1,72 @@ +dm->persist($doc); + $this->dm->flush(); + + // place element '0' after '1' + $collection = new \Doctrine\Common\Collections\ArrayCollection(array( + $collection[1], + $collection[0], + $collection[2] + )); + + $doc->set($collection); + + // changing value together with reordering causes issue when saving: + $collection[1]->set('tmp'); + + $this->dm->persist($doc); + $this->dm->flush(null, array('safe' => true)); + + $this->dm->refresh($doc); + + $array = array(); + foreach($doc->get() as $value) { + $array[] = $value->get(); + } + $this->assertEquals(array('1', 'tmp', '2'), $array); + } +} + +/** @ODM\Document(collection="tests", db="tests") */ +class MODM29Doc +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\EmbedMany(targetDocument="MODM29Embedded", strategy="set") */ + protected $collection; + + function __construct($c) {$this->set($c);} + + function set($c) {$this->collection = $c;} + function get() {return $this->collection;} +} + +/** @ODM\EmbeddedDocument */ +class MODM29Embedded +{ + /** @ODM\String */ + protected $val; + + function __construct($val) {$this->set($val);} + function get() {return $this->val;} + function set($val) {$this->val = $val;} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test1.txt b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test1.txt new file mode 100644 index 00000000..e69de29b diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test2.txt b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42/test2.txt new file mode 100644 index 00000000..e69de29b diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42Test.php new file mode 100644 index 00000000..f22f94a8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM42Test.php @@ -0,0 +1,86 @@ +dm->persist($file1); + $this->dm->persist($file2); + $this->dm->persist($dir); + + $this->assertTrue($this->dm->getUnitOfWork()->isScheduledForInsert($dir)); + $this->assertTrue($this->dm->getUnitOfWork()->isScheduledForInsert($file1)); + $this->assertTrue($this->dm->getUnitOfWork()->isScheduledForInsert($file2)); + + $this->dm->flush(); + $this->dm->clear(); + + $dir = $this->dm->find(__NAMESPACE__.'\Directory', $dir->getId()); + $this->assertNotNull($dir); + $this->assertEquals(2, count($dir->getFiles())); + foreach($dir->getFiles() as $file) { + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $file->getMongoFile()); + } + } +} + +/** @ODM\Document(collection="modm42_directories") */ +class Directory +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $test = 'test'; + + /** @ODM\ReferenceMany(targetDocument="File") */ + protected $files = array(); + + public function __construct($files) + { + $this->files = $files; + } + + public function getId() + { + return $this->id; + } + + public function getFiles() + { + return $this->files; + } +} + +/** @ODM\Document(collection="modm42_files") */ +class File +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\File */ + protected $file; + + public function __construct($path) + { + $this->file = $path; + } + + public function getId() + { + return $this->id; + } + + public function getMongoFile() + { + return $this->file; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM43Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM43Test.php new file mode 100644 index 00000000..6c0360fa --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM43Test.php @@ -0,0 +1,42 @@ + 'Jonathan Wage' + ); + $this->dm->getConnection()->modm43_test->people->insert($person); + $user = $this->dm->find(__NAMESPACE__.'\Person', $person['_id']); + $this->assertEquals('Jonathan', $user->firstName); + $this->assertEquals('Wage', $user->lastName); + } +} + +/** @ODM\Document(db="modm43_test", collection="people") */ +class Person +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $firstName; + + /** @ODM\String */ + public $lastName; + + /** @ODM\PreLoad */ + public function preLoad(array &$data) + { + if (isset($data['name'])) { + $e = explode(' ', $data['name']); + $data['firstName'] = $e[0]; + $data['lastName'] = $e[1]; + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM45Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM45Test.php new file mode 100644 index 00000000..302f9fbb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM45Test.php @@ -0,0 +1,45 @@ +setB(new MODM45B()); + + $this->dm->persist($a); + $this->dm->flush(); + $this->dm->clear(); + + $a = $this->dm->find(__NAMESPACE__.'\MODM45A', $a->getId()); + $c = (null !== $a->getB()); + $this->assertTrue($c); // returns false, while expecting true + } +} + +/** @ODM\Document(collection="modm45_test") */ +class MODM45A +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\EmbedOne(targetDocument="MODM45B") */ + protected $b; + + function getId() {return $this->id;} + function getB() {return $this->b;} + function setB($b) {$this->b = $b;} +} + +/** @ODM\EmbeddedDocument */ +class MODM45B +{ + /** @ODM\String */ + protected $val; + function setVal($val) {$this->val = $val;} + function getVal() {return $this->val;} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM46Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM46Test.php new file mode 100644 index 00000000..f6eeda6f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM46Test.php @@ -0,0 +1,41 @@ + array('value' => 'value') + ); + $this->dm->getConnection()->modm46_test->a->insert($a); + + $a = $this->dm->find(__NAMESPACE__.'\MODM46A', $a['_id']); + + $this->assertTrue(isset($a->b)); + $this->assertEquals('value', $a->b->value); + } +} + +/** @ODM\Document(db="modm46_test", collection="a") */ +class MODM46A +{ + /** @ODM\Id */ + public $id; + + /** + * @ODM\EmbedOne(targetDocument="MODM46AB") + * @ODM\AlsoLoad("c") + */ + public $b; +} + +/** @ODM\EmbeddedDocument */ +class MODM46AB +{ + /** @ODM\String */ + public $value; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM47Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM47Test.php new file mode 100644 index 00000000..181c840b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM47Test.php @@ -0,0 +1,33 @@ + 'c value' + ); + $this->dm->getConnection()->modm47_test->a->insert($a); + + $a = $this->dm->find(__NAMESPACE__.'\MODM47A', $a['_id']); + $this->assertEquals('c value', $a->b); + } +} + +/** @ODM\Document(db="modm47_test", collection="a") */ +class MODM47A +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $b = 'tmp'; + + /** @ODM\AlsoLoad("c") */ + function renameC($c) {$this->b = $c;} + function getId() {return $this->id;} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM48Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM48Test.php new file mode 100644 index 00000000..b6e2335d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM48Test.php @@ -0,0 +1,52 @@ +b = new MODM48B(); + $this->dm->persist($a); + $this->dm->flush(); + $this->dm->clear(); + + $a = $this->dm->find(__NAMESPACE__.'\MODM48A', $a->id); + $this->assertNotNull($a); + + $a->getB()->setVal('test'); + + $this->dm->flush(null, array('safe' => true)); + $this->dm->clear(); + + $a = $this->dm->find(__NAMESPACE__.'\MODM48A', $a->id); + $this->assertEquals('test', $a->getB()->getVal()); + } +} + +/** @ODM\Document(db="modm48_tests", collection="a") */ +class MODM48A +{ + /** @ODM\Id */ + public $id; + + /** @ODM\EmbedOne(targetDocument="MODM48B") */ + public $b; + + function getId() {return $this->id;} + function getB() {return $this->b;} + function setB($b) {$this->b = $b;} +} + +/** @ODM\EmbeddedDocument */ +class MODM48B +{ + /** @ODM\String */ + public $val; + + function setVal($val) {$this->val = $val;} + function getVal() {return $this->val;} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50/test.txt b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50/test.txt new file mode 100644 index 00000000..8318c86b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50/test.txt @@ -0,0 +1 @@ +Test \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50Test.php new file mode 100644 index 00000000..7440f81b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM50Test.php @@ -0,0 +1,42 @@ +dm->persist($image); + $this->dm->flush(); + + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $image->file); + } +} + +/** + * @ODM\Document(collection="files", db="modm50_tests") + * @ODM\InheritanceType("SINGLE_COLLECTION") + * @ODM\DiscriminatorField(fieldName="type") + * @ODM\DiscriminatorMap({ + * "file"="MODM50File", + * "image"="MODM50Image" + * }) + */ +class MODM50File +{ + /** @ODM\Id */ + public $id; + + /** @ODM\File */ + public $file; + + function __construct($file) {$this->file = $file;} +} + +/** @ODM\Document(collection="files", db="modm50_tests") */ +class MODM50Image extends MODM50File +{ +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM52Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM52Test.php new file mode 100644 index 00000000..316c2de9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM52Test.php @@ -0,0 +1,77 @@ +dm->persist($doc); + $this->dm->flush(null, array('safe' => true)); + + $this->dm->refresh($doc); + + // change nested embedded collection: + $doc->getItem(0)->removeItem(1); + $before = count($doc->getItem(0)->getItems()); + + $this->dm->persist($doc); + $this->dm->flush(); + $this->dm->refresh($doc); + + $after = count($doc->getItem(0)->getItems()); + $this->assertEquals(1, $before); + $this->assertEquals(1, $after); + } +} + +/** + * @ODM\MappedSuperClass + */ +class MODM52Container +{ + /** @ODM\String */ + public $value; + + /** @ODM\EmbedMany(targetDocument="MODM52Embedded", strategy="set") */ + public $items = array(); + + public function __construct($items = null, $value = null) + { + if ($items) { + $this->items = $items; + } + $this->value = $value; + } + + public function getItems() + { + return $this->items; + } + + public function getItem($index) + { + return $this->items[$index]; + } + + public function removeItem($i) + { + unset($this->items[$i]); + } +} + +/** @ODM\EmbeddedDocument */ +class MODM52Embedded extends MODM52Container +{} + +/** @ODM\Document(db="tests", collection="tests") */ +class MODM52Doc extends MODM52Container +{ + /** @ODM\Id */ + protected $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM56Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM56Test.php new file mode 100644 index 00000000..4babbe71 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM56Test.php @@ -0,0 +1,72 @@ +dm->persist($parent); + $this->dm->flush(); + + $childOne = new MODM56Child('Child One'); + $parent->children[] = $childOne; + + $childTwo = new MODM56Child('Child Two'); + $parent->children[] = $childTwo; + $this->dm->flush(null, array('safe' => true)); + + $test = $this->dm->getDocumentCollection(__NAMESPACE__.'\MODM56Parent')->findOne(); + + $this->assertEquals('Parent', $test['name']); + $this->assertInstanceOf('\MongoDate', $test['updatedAt']); + $this->assertEquals(2, count($test['children'])); + $this->assertEquals('Child One', $test['children'][0]['name']); + $this->assertEquals('Child Two', $test['children'][1]['name']); + } +} + +/** @ODM\Document */ +class MODM56Parent +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\Date */ + public $updatedAt; + + /** @ODM\EmbedMany(targetDocument="MODM56Child") */ + public $children = array(); + + public function __construct($name) + { + $this->name = $name; + } + + /** @ODM\PreUpdate */ + public function preUpdate() + { + $this->updatedAt = new \DateTime(); + } +} + +/** @ODM\EmbeddedDocument */ +class MODM56Child +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM62Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM62Test.php new file mode 100644 index 00000000..1803d4e1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM62Test.php @@ -0,0 +1,35 @@ +dm->persist($test); + $this->dm->flush(); + $this->dm->refresh($test); + + $test->setB(array('test', 'test2')); + $this->dm->flush(); + $this->dm->clear(); + + $test = $this->dm->find(__NAMESPACE__.'\MODM62Document', $test->id); + $this->assertEquals(array('test', 'test2'), $test->b); + } +} + +/** @ODM\Document(collection="modm62_users") */ +class MODM62Document +{ + /** @ODM\Id */ + public $id; + + /** @ODM\Collection */ + public $b = array('ok'); + + public function setB($b) {$this->b = $b;} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM65Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM65Test.php new file mode 100644 index 00000000..d02433a3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM65Test.php @@ -0,0 +1,65 @@ +socialNetworkUser = new MODM65SocialNetworkUser(); + $user->socialNetworkUser->firstName = 'Jonathan'; + $user->socialNetworkUser->lastName = 'Wage'; + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getDocumentCollection(__NAMESPACE__.'\MODM65User')->findOne(); + $this->assertTrue(isset($user['snu']['lN'])); + $this->assertTrue(isset($user['snu']['fN'])); + + $user = $this->dm->find(__NAMESPACE__.'\MODM65User', $user['_id']); + $this->assertEquals('Jonathan', $user->socialNetworkUser->firstName); + $this->assertEquals('Wage', $user->socialNetworkUser->lastName); + } +} + +/** + * @ODM\Document(collection="modm65_users") + */ +class MODM65User +{ + /** + * @ODM\Id + */ + public $id; + /** + * @ODM\EmbedOne( + * discriminatorField="php", + * discriminatorMap={ + * "fbu"="Doctrine\ODM\MongoDB\Tests\Functional\Ticket\MODM65SocialNetworkUser" + * }, + * name="snu" + * ) + */ + public $socialNetworkUser; +} + +/** + * @ODM\EmbeddedDocument + */ +class MODM65SocialNetworkUser +{ + /** + * @ODM\String(name="fN") + * @var string + */ + public $firstName; + /** + * @ODM\String(name="lN") + * @var string + */ + public $lastName; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM66Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM66Test.php new file mode 100644 index 00000000..acdba3ce --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM66Test.php @@ -0,0 +1,99 @@ +dm->persist($a); + $this->dm->flush(); + $b2 = new B('second'); + $a->getB()->add($b2); + $this->dm->flush(); + + $this->dm->refresh($a); + $b = $a->getB()->toArray(); + + $this->assertEquals(2, count($b)); + + $this->assertEquals(array( + $b1->getId(), $b2->getId() + ), array( + $b[0]->getId(), $b[1]->getId() + )); + } + + public function testRefresh() + { + $b1 = new B('first'); + $a = new A(array($b1)); + $this->dm->persist($a); + $this->dm->flush(); + $b2 = new B('second'); + + $this->dm->refresh($a); + + $a->getB()->add($b2); + $this->dm->flush(); + $this->dm->refresh($a); + $b = $a->getB()->toArray(); + + $this->assertEquals(2, count($b)); + + $this->assertEquals(array( + $b1->getId(), $b2->getId() + ), array( + $b[0]->getId(), $b[1]->getId() + )); + } + +} + +/** @ODM\Document(db="tests", collection="tests") */ +class A +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\ReferenceMany(targetDocument="b", cascade="all") */ + protected $b; + + function __construct($b) + { + $this->b = new ArrayCollection($b); + } + + function getB() + { + return $this->b; + } +} + +/** @ODM\Document(db="tests", collection="tests2") */ +class B +{ + + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $value; + + function __construct($v) + { + $this->value = $v; + } + + public function getId() + { + return $this->id; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM67Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM67Test.php new file mode 100644 index 00000000..684ce1c2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM67Test.php @@ -0,0 +1,127 @@ +listener = new MODM67TestEventListener($this->dm); + $evm = $this->dm->getEventManager(); + $events = array( + Events::prePersist, + Events::postPersist, + Events::preUpdate, + Events::postUpdate, + ); + $evm->addEventListener($events, $this->listener); + + return $this->dm; + } + + public function testDerivedClassListener() + { + $dm = $this->getDocumentManager(); + + $testDoc = new MODM67DerivedClass(); + $testDoc->embedOne = new MODM67EmbeddedObject(); + + $dm->persist($testDoc); + $dm->flush(null, array('safe' => true)); + + $this->assertTrue($testDoc->embedOne->prePersist); + $this->assertTrue($testDoc->embedOne->postPersist); + + $this->assertFalse($testDoc->embedOne->preUpdate); + $this->assertFalse($testDoc->embedOne->postUpdate); + + $dm->clear(); + + $testDoc = $dm->find(__NAMESPACE__ . '\MODM67DerivedClass', $testDoc->id); + $testDoc->embedOne->numAccesses = 1; + $dm->flush(null, array('safe' => true)); + + $this->assertTrue($testDoc->embedOne->preUpdate); + $this->assertTrue($testDoc->embedOne->postUpdate); + } +} + +class MODM67TestEventListener +{ + public $documentManager; + + public function __construct(DocumentManager $documentManager) + { + $this->documentManager = $documentManager; + } + + public function prePersist(LifecycleEventArgs $eventArgs) + { + $document = $eventArgs->getDocument(); + if ($document instanceof MODM67EmbeddedObject) { + $document->prePersist = true; + } + } + + public function postPersist(LifecycleEventArgs $eventArgs) + { + $document = $eventArgs->getDocument(); + if ($document instanceof MODM67EmbeddedObject) { + $document->postPersist = true; + } + } + + public function preUpdate(LifecycleEventArgs $eventArgs) + { + $document = $eventArgs->getDocument(); + if ($document instanceof MODM67EmbeddedObject) { + $document->preUpdate = true; + } + } + + public function postUpdate(LifecycleEventArgs $eventArgs) + { + $document = $eventArgs->getDocument(); + if ($document instanceof MODM67EmbeddedObject) { + $document->postUpdate = true; + } + } +} + +/** + * @ODM\Document + */ +class MODM67DerivedClass +{ + /** @ODM\Id */ + public $id; + + /** @ODM\EmbedOne(targetDocument="MODM67EmbeddedObject") */ + public $embedOne; +} + +/** + * @ODM\EmbeddedDocument + */ +class MODM67EmbeddedObject +{ + /** @ODM\Int */ + public $numAccesses = 0; + + /** @ODM\Boolean */ + public $prePersist = false; + + /** @ODM\Boolean */ + public $postPersist = false; + + /** @ODM\Boolean */ + public $preUpdate = false; + + /** @ODM\Boolean */ + public $postUpdate = false; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM70Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM70Test.php new file mode 100644 index 00000000..41f916ab --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM70Test.php @@ -0,0 +1,142 @@ +dm->persist($avatar); + $this->dm->flush(); + $this->dm->refresh($avatar); + + $avatar->addAvatarPart(new AvatarPart('#FFF')); + + $this->dm->flush(); + $this->dm->refresh($avatar); + + $parts = $avatar->getAvatarParts(); + $this->assertEquals(2, count($parts)); + $this->assertEquals('#FFF', $parts[1]->getColor()); + } +} + +/** + * @ODM\Document(db="tests", collection="avatars") + */ +class Avatar +{ + + /** + * @ODM\Id + */ + protected $id; + + /** + * @ODM\String(name="na") + * @var string + */ + protected $name; + + /** + * @ODM\Int(name="sex") + * @var int + */ + protected $sex; + + /** + * @ODM\EmbedMany( + * targetDocument="AvatarPart", + * name="aP" + * ) + * @var array AvatarPart + */ + protected $avatarParts; + + public function __construct($name, $sex, $avatarParts = null) + { + $this->name = $name; + $this->sex = $sex; + $this->avatarParts = $avatarParts; + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getSex() + { + return $this->sex; + } + + public function setSex($sex) + { + $this->sex = $sex; + } + + public function getAvatarParts() + { + return $this->avatarParts; + } + + public function addAvatarPart($part) + { + $this->avatarParts[] = $part; + } + + public function setAvatarParts($parts) + { + $this->avatarParts = $parts; + } + + public function removeAvatarPart($part) + { + $key = array_search($this->avatarParts, $part); + if ($key !== false) { + unset($this->avatarParts[$key]); + } + } +} + +/** + * @ODM\EmbeddedDocument + */ +class AvatarPart +{ + /** + * @ODM\String(name="col") + * @var string + */ + protected $color; + + public function __construct($color = null) + { + $this->color = $color; + } + + public function getColor() + { + return $this->color; + } + + public function setColor($color) + { + $this->color = $color; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM72Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM72Test.php new file mode 100644 index 00000000..e945c6a1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM72Test.php @@ -0,0 +1,24 @@ +dm->getClassMetadata(__NAMESPACE__.'\MODM72User'); + $this->assertEquals(array('test' => 'test'), $class->fieldMappings['name']['options']); + } +} + +/** @ODM\Document */ +class MODM72User +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String(options={"test"="test"}) */ + public $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM76Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM76Test.php new file mode 100644 index 00000000..936a64cb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM76Test.php @@ -0,0 +1,84 @@ +dm->persist($a); + $this->dm->flush(); + + $this->assertTrue($a->getId() != null); + } +} + +/** @ODM\Document(db="tests", collection="tests") */ +class MODM76A +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $test = 'test'; + + /** @ODM\EmbedMany(targetDocument="MODM76B") */ + protected $b = array(); + + /** @ODM\ReferenceMany(targetDocument="MODM76C") */ + protected $c = array(); + + public function __construct($b, $c) + { + $this->b = new ArrayCollection($b); + $this->c = new ArrayCollection($c); + } + + public function getB() + { + return $this->b; + } + + public function getC() + { + return $this->c; + } + + public function getId() + { + return $this->id; + } +} + +/** @ODM\EmbeddedDocument */ +class MODM76B +{ + /** @ODM\ReferenceOne(targetDocument="MODM76C") */ + protected $c; + + public function __construct($c) + { + $this->c = $c; + } + + public function getC() + { + return $this->c; + } +} + +/** @ODM\Document(db="tests", collection="tests2") */ +class MODM76C +{ + /** @ODM\Id */ + protected $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM81Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM81Test.php new file mode 100644 index 00000000..69a5c3ba --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM81Test.php @@ -0,0 +1,158 @@ +dm; + } + + public function testDocumentIdWithSameProxyId() + { + $dm = $this->getDocumentManager(); + + $doc1 = new MODM81TestDocument(); + $doc1->setName('Document1'); + + $doc2 = new MODM81TestDocument(); + $doc2->setName('Document2'); + + $dm->persist($doc1); + $dm->persist($doc2); + $dm->flush(); + + $embedded = new MODM81TestEmbeddedDocument($doc1, $doc2, 'Test1'); + $doc1->setEmbeddedDocuments(array($embedded)); + $doc2->setEmbeddedDocuments(array($embedded)); + + $dm->flush(); + $dm->clear(); + + $doc1 = $dm->find(__NAMESPACE__ . '\MODM81TestDocument', $doc1->getId()); + $doc1->setName('Document1Change'); + + $this->assertSame($doc1, $doc1->getEmbeddedDocuments()->get(0)->getRefTodocument1()); + + $dm->flush(); + $dm->clear(); + + $doc1 = $dm->find(__NAMESPACE__ . '\MODM81TestDocument', $doc1->getId()); + $this->assertNotNull($doc1); + $this->assertEquals('Document1Change', $doc1->getName()); + + $doc1->getEmbeddedDocuments()->get(0)->getRefTodocument1()->setName('Document1ProxyChange'); + + $dm->flush(); + $dm->clear(); + + $doc1 = $dm->find(__NAMESPACE__ . '\MODM81TestDocument', $doc1->getId()); + $this->assertEquals('Document1ProxyChange', $doc1->getName()); + } +} + +/** @ODM\Document */ +class MODM81TestDocument +{ + /** @ODM\Id */ + protected $id; + + /** @ODM\String */ + protected $name; + + /** @ODM\EmbedMany(targetDocument="MODM81TestEmbeddedDocument") */ + protected $embeddedDocuments; + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function getEmbeddedDocuments() + { + return $this->embeddedDocuments; + } + + /** + * @param array $documents + */ + public function setEmbeddedDocuments($documents) + { + $this->embeddedDocuments = new ArrayCollection($documents); + } + +} + +/** @ODM\EmbeddedDocument */ +class MODM81TestEmbeddedDocument +{ + /** @ODM\String */ + public $message; + + /** @ODM\ReferenceOne(targetDocument="MODM81TestDocument") */ + public $refTodocument1; + + /** @ODM\ReferenceOne(targetDocument="MODM81TestDocument") */ + public $refTodocument2; + + public function __construct($document1, $document2, $message) + { + $this->refTodocument1 = $document1; + $this->refTodocument2 = $document2; + $this->message = $message; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return MODM81TestDocument + */ + public function getRefTodocument1() + { + return $this->refTodocument1; + } + + /** + * @return MODM81TestDocument + */ + public function getRefTodocument2() + { + return $this->refTodocument2; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php new file mode 100644 index 00000000..7a27ae31 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php @@ -0,0 +1,90 @@ +listener = new MODM83EventListener(); + $evm = $this->dm->getEventManager(); + $events = array( + Events::preUpdate, + Events::postUpdate, + ); + $evm->addEventListener($events, $this->listener); + return $this->dm; + } + + public function testDocumentWithEmbeddedDocumentNotUpdated() + { + $dm = $this->getDocumentManager(); + + $won = new MODM83TestDocument(); + $won->name = 'Parent'; + $won->embedded = new MODM83TestEmbeddedDocument(); + $won->embedded->name = 'Child'; + $too = new MODM83OtherDocument(); + $too->name = 'Neighbor'; + $dm->persist($won); + $dm->persist($too); + $dm->flush(); + $dm->clear(); + + $won = $dm->find(__NAMESPACE__.'\MODM83TestDocument', $won->id); + $too = $dm->find(__NAMESPACE__.'\MODM83OtherDocument', $too->id); + $too->name = 'Bob'; + $dm->flush(); + $dm->clear(); + + $called = array( + Events::preUpdate => array(__NAMESPACE__.'\MODM83OtherDocument'), + Events::postUpdate => array(__NAMESPACE__.'\MODM83OtherDocument') + ); + $this->assertEquals($called, $this->listener->called); + } +} + +class MODM83EventListener +{ + public $called = array(); + public function __call($method, $args) + { + $document = $args[0]->getDocument(); + $className = get_class($document); + $this->called[$method][] = $className; + } +} + +/** @ODM\Document */ +class MODM83TestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedOne(targetDocument="MODM83TestEmbeddedDocument") */ + public $embedded; +} + +/** @ODM\EmbeddedDocument */ +class MODM83TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; +} + +/** @ODM\Document */ +class MODM83OtherDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM88Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM88Test.php new file mode 100644 index 00000000..b4781b3d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM88Test.php @@ -0,0 +1,31 @@ +setTitle('Test Title'); + $article->setBody('Test Body'); + $this->dm->persist($article); + $this->dm->flush(); + $this->dm->clear(); + + $qb = $this->dm->createQueryBuilder('Documents\Article') + ->select('_id', 'title'); + $q = $qb->getQuery(); + $document = $q->getSingleResult(); + + $this->assertEquals('Test Title', $document->getTitle()); + $this->assertNull($document->getBody()); + + $document->setTitle('changed'); + $this->dm->flush(); + + $check = $this->dm->getDocumentCollection('Documents\Article')->findOne(); + $this->assertEquals('changed', $check['title']); + $this->assertEquals('Test Body', $check['body']); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php new file mode 100644 index 00000000..7e42af6d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php @@ -0,0 +1,115 @@ +listener = new MODM90EventListener(); + $evm = $this->dm->getEventManager(); + $events = array( + Events::preUpdate, + Events::postUpdate, + ); + $evm->addEventListener($events, $this->listener); + return $this->dm; + } + + public function testDocumentWithEmbeddedDocumentNotUpdatedOnFlush() + { + $dm = $this->getDocumentManager(); + + $testDoc = new MODM90TestDocument(); + $testDoc->name = 'Parent'; + $testDoc->embedded = new MODM90TestEmbeddedDocument(); + $testDoc->embedded->name = 'Child'; + $dm->persist($testDoc); + $dm->flush(); + $dm->clear(); + + $testDoc = $dm->find(__NAMESPACE__.'\MODM90TestDocument', $testDoc->id); + + // run a flush, in theory, nothing should be flushed. + $dm->flush(); + $dm->clear(); + + // no update events should be called + $called = array(); + $this->assertEquals($called, $this->listener->called); + } + + /* + * Ensures that the descriminator field is not unset if it's a + * real property on the document. + */ + public function testDiscriminatorFieldValuePresentIfRealProperty() + { + $dm = $this->getDocumentManager(); + + $testDoc = new MODM90TestDocument(); + $testDoc->name = 'Parent'; + $testDoc->embedded = new MODM90Test2EmbeddedDocument(); + $testDoc->embedded->name = 'Child'; + $dm->persist($testDoc); + $dm->flush(); + $dm->clear(); + + $testDoc = $dm->find(__NAMESPACE__.'\MODM90TestDocument', $testDoc->id); + + $this->assertEquals($testDoc->embedded->type, 'test2'); + } +} + +class MODM90EventListener +{ + public $called = array(); + public function __call($method, $args) + { + $document = $args[0]->getDocument(); + $className = get_class($document); + $this->called[$method][] = $className; + } +} + +/** @ODM\Document */ +class MODM90TestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** + * @ODM\EmbedOne + * ( + * discriminatorField="type", + * discriminatorMap={ + * "test"="MODM90TestEmbeddedDocument", + * "test2"="MODM90Test2EmbeddedDocument" + * } + * ) + */ + public $embedded; +} + +/** @ODM\EmbeddedDocument */ +class MODM90TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; +} + +/** @ODM\EmbeddedDocument */ +class MODM90Test2EmbeddedDocument +{ + /** @ODM\String */ + public $name; + + /** @ODM\String The discriminator field is a real property */ + public $type; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php new file mode 100644 index 00000000..4f33f03e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php @@ -0,0 +1,72 @@ +listener = new MODM91EventListener(); + $evm = $this->dm->getEventManager(); + $events = array( + Events::preUpdate, + Events::postUpdate, + ); + $evm->addEventListener($events, $this->listener); + return $this->dm; + } + + public function testDocumentWithEmbeddedDocumentNotUpdated() + { + $dm = $this->getDocumentManager(); + + $testDoc = new MODM91TestDocument(); + $testDoc->name = 'test doc'; + $testDoc->embedded = new MODM91TestEmbeddedDocument(); + + $dm->persist($testDoc); + $dm->flush(); + $dm->clear(); + + $testDoc = $dm->find(__NAMESPACE__.'\MODM91TestDocument', $testDoc->id); + $dm->flush(); + $dm->clear(); + + $called = array(); + $this->assertEquals($called, $this->listener->called); + } +} + +class MODM91EventListener +{ + public $called = array(); + public function __call($method, $args) + { + $document = $args[0]->getDocument(); + $className = get_class($document); + $this->called[$method][] = $className; + } +} + +/** @ODM\Document */ +class MODM91TestDocument +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\EmbedOne(targetDocument="MODM91TestEmbeddedDocument") */ + public $embedded; +} + +/** @ODM\EmbeddedDocument */ +class MODM91TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM92Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM92Test.php new file mode 100644 index 00000000..62c4c264 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM92Test.php @@ -0,0 +1,83 @@ +setEmbeddedDocuments($embeddedDocuments); + $this->dm->persist($testDoc); + $this->dm->flush(); + $this->dm->clear(); + + $testDoc = $this->dm->find(__NAMESPACE__.'\MODM92TestDocument', $testDoc->id); + $this->assertEquals($embeddedDocuments, $testDoc->embeddedDocuments->toArray()); + + $embeddedDocuments = array(new MODM92TestEmbeddedDocument('bar')); + + $testDoc->setEmbeddedDocuments($embeddedDocuments); + $this->assertEquals($embeddedDocuments, $testDoc->embeddedDocuments->toArray()); + + $this->dm->flush(); + $this->dm->clear(); + $testDoc = $this->dm->find(__NAMESPACE__.'\MODM92TestDocument', $testDoc->id); + + $this->assertEquals($embeddedDocuments, $testDoc->embeddedDocuments->toArray()); + } +} + +/** @ODM\Document */ +class MODM92TestDocument +{ + /** @ODM\Id */ + public $id; + + // Note: Test case fails with default "pushAll" strategy, but "set" works + /** @ODM\EmbedMany(targetDocument="MODM92TestEmbeddedDocument") */ + public $embeddedDocuments; + + public function __construct() { + $this->embeddedDocuments = new ArrayCollection(); + } + + /** + * Sets children + * + * If $images is not an array or Traversable object, this method will simply + * clear the images collection property. If any elements in the parameter + * are not an Image object, this method will attempt to convert them to one + * by mapping array indexes (size URL's are required, cropMetadata is not). + * Any invalid elements will be ignored. + * + * @param array|Traversable $children + */ + public function setEmbeddedDocuments($embeddedDocuments) { + $this->embeddedDocuments->clear(); + + if (! (is_array($embeddedDocuments) || $embeddedDocuments instanceof \Traversable)) { + return; + } + + foreach ($embeddedDocuments as $embeddedDocument) { + $this->embeddedDocuments->add($embeddedDocument); + } + } +} + +/** @ODM\EmbeddedDocument */ +class MODM92TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; + + public function __construct($name) { + $this->name = $name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM95Test.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM95Test.php new file mode 100644 index 00000000..3e5ececd --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM95Test.php @@ -0,0 +1,89 @@ +setEmbeddedDocuments($embeddedDocuments); + $this->dm->persist($testDoc); + $this->dm->flush(); + $this->dm->clear(); + + $testDoc = $this->dm->find(__NAMESPACE__.'\MODM95TestDocument', $testDoc->id); + + $this->assertEquals($embeddedDocuments, $testDoc->embeddedDocuments->toArray()); + + $this->dm->remove($testDoc); + $this->dm->flush(); + $this->dm->clear(); + + $testDocLoad = $this->dm->find(__NAMESPACE__.'\MODM95TestDocument', $testDoc->id); + $this->assertNull($testDocLoad); + + $this->dm->persist($testDoc); + $this->dm->flush(); + $this->dm->clear(); + + $testDocLoad = $this->dm->find(__NAMESPACE__.'\MODM95TestDocument', $testDoc->id); + $this->assertNotNull($testDocLoad); + + $this->assertEquals($embeddedDocuments, $testDocLoad->embeddedDocuments->toArray()); + } +} + +/** @ODM\Document */ +class MODM95TestDocument +{ + /** @ODM\Id */ + public $id; + + // Note: Test case fails with default "pushAll" strategy, but "set" works + /** @ODM\EmbedMany(targetDocument="MODM95TestEmbeddedDocument") */ + public $embeddedDocuments; + + public function __construct() { + $this->embeddedDocuments = new ArrayCollection(); + } + + /** + * Sets children + * + * If $images is not an array or Traversable object, this method will simply + * clear the images collection property. If any elements in the parameter + * are not an Image object, this method will attempt to convert them to one + * by mapping array indexes (size URL's are required, cropMetadata is not). + * Any invalid elements will be ignored. + * + * @param array|Traversable $children + */ + public function setEmbeddedDocuments($embeddedDocuments) { + $this->embeddedDocuments->clear(); + + if (! (is_array($embeddedDocuments) || $embeddedDocuments instanceof \Traversable)) { + return; + } + + foreach ($embeddedDocuments as $embeddedDocument) { + $this->embeddedDocuments->add($embeddedDocument); + } + } +} + +/** @ODM\EmbeddedDocument */ +class MODM95TestEmbeddedDocument +{ + /** @ODM\String */ + public $name; + + public function __construct($name) { + $this->name = $name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/file.txt b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/file.txt new file mode 100644 index 00000000..b7801914 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Functional/file.txt @@ -0,0 +1 @@ +These are the bytes... \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php new file mode 100644 index 00000000..36e60678 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -0,0 +1,98 @@ +dm->getClassMetadata(__NAMESPACE__.'\HydrationClosureUser'); + + $user = new HydrationClosureUser(); + $this->dm->getHydratorFactory()->hydrate($user, array( + '_id' => 1, + 'name' => 'jon', + 'referenceOne' => array('$id' => '1'), + 'referenceMany' => array( + array( + '$id' => '1' + ), + array( + '$id' => '2' + ) + ), + 'embedOne' => array('name' => 'jon'), + 'embedMany' => array( + array('name' => 'jon') + ) + )); + + $this->assertEquals(1, $user->id); + $this->assertEquals('jon', $user->name); + $this->assertInstanceOf(__NAMESPACE__.'\HydrationClosureReferenceOne', $user->referenceOne); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\PersistentCollection', $user->referenceMany); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Proxy\Proxy', $user->referenceMany[0]); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Proxy\Proxy', $user->referenceMany[1]); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\PersistentCollection', $user->embedMany); + $this->assertEquals('jon', $user->embedOne->name); + $this->assertEquals('jon', $user->embedMany[0]->name); + } +} + +/** @ODM\Document */ +class HydrationClosureUser +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; + + /** @ODM\ReferenceOne(targetDocument="HydrationClosureReferenceOne") */ + public $referenceOne; + + /** @ODM\ReferenceMany(targetDocument="HydrationClosureReferenceMany") */ + public $referenceMany = array(); + + /** @ODM\EmbedOne(targetDocument="HydrationClosureEmbedOne") */ + public $embedOne; + + /** @ODM\EmbedMany(targetDocument="HydrationClosureEmbedMany") */ + public $embedMany = array(); +} + +/** @ODM\Document */ +class HydrationClosureReferenceOne +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; +} + +/** @ODM\Document */ +class HydrationClosureReferenceMany +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $name; +} + +/** @ODM\EmbeddedDocument */ +class HydrationClosureEmbedMany +{ + /** @ODM\String */ + public $name; +} + +/** @ODM\EmbeddedDocument */ +class HydrationClosureEmbedOne +{ + /** @ODM\String */ + public $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTest.php new file mode 100644 index 00000000..4af0c9fa --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTest.php @@ -0,0 +1,444 @@ +_loadDriver(); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + + return $class; + } + + /** + * @depends testLoadMapping + * @param ClassMetadata $class + */ + public function testDocumentCollectionNameAndInheritance($class) + { + $this->assertEquals('cms_users', $class->getCollection()); + $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $class->inheritanceType); + + return $class; + } + + /** + * @depends testDocumentCollectionNameAndInheritance + * @param ClassMetadata $class + */ + public function testFieldMappings($class) + { + $this->assertEquals(10, count($class->fieldMappings)); + $this->assertTrue(isset($class->fieldMappings['id'])); + $this->assertTrue(isset($class->fieldMappings['name'])); + $this->assertTrue(isset($class->fieldMappings['email'])); + + return $class; + } + + /** + * @depends testDocumentCollectionNameAndInheritance + * @param ClassMetadata $class + */ + public function testAssociationMappings($class) + { + $this->assertEquals(6, count($class->associationMappings)); + $this->assertTrue(isset($class->associationMappings['address'])); + $this->assertTrue(isset($class->associationMappings['phonenumbers'])); + $this->assertTrue(isset($class->associationMappings['groups'])); + $this->assertTrue(isset($class->associationMappings['morePhoneNumbers'])); + $this->assertTrue(isset($class->associationMappings['embeddedPhonenumber'])); + $this->assertTrue(isset($class->associationMappings['otherPhonenumbers'])); + } + + /** + * @depends testDocumentCollectionNameAndInheritance + * @param ClassMetadata $class + */ + public function testGetAssociationTargetClass($class) + { + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Address', $class->getAssociationTargetClass('address')); + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Group', $class->getAssociationTargetClass('groups')); + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Phonenumber', $class->getAssociationTargetClass('phonenumbers')); + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Phonenumber', $class->getAssociationTargetClass('morePhoneNumbers')); + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Phonenumber', $class->getAssociationTargetClass('embeddedPhonenumber')); + $this->assertEquals('Doctrine\ODM\MongoDB\Tests\Mapping\Phonenumber', $class->getAssociationTargetClass('otherPhonenumbers')); + } + + /** + * @depends testDocumentCollectionNameAndInheritance + * @expectedException \InvalidArgumentException + * @param ClassMetadata $class + */ + public function testGetAssociationTargetClassThrowsExceptionWhenEmpty($class) + { + $class->getAssociationTargetClass('invalid_association'); + } + + /** + * @depends testDocumentCollectionNameAndInheritance + * @param ClassMetadata $class + */ + public function testStringFieldMappings($class) + { + $this->assertEquals('string', $class->fieldMappings['name']['type']); + + return $class; + } + + /** + * @depends testFieldMappings + * @param ClassMetadata $class + */ + public function testIdentifier($class) + { + $this->assertEquals('id', $class->identifier); + + return $class; + } + + /** + * @depends testIdentifier + * @param ClassMetadata $class + */ + public function testAssocations($class) + { + $this->assertEquals(10, count($class->fieldMappings)); + + return $class; + } + + /** + * @depends testAssocations + * @param ClassMetadata $class + */ + public function testOwningOneToOneAssocation($class) + { + $this->assertTrue(isset($class->fieldMappings['address'])); + $this->assertTrue(is_array($class->fieldMappings['address'])); + // Check cascading + $this->assertTrue($class->fieldMappings['address']['isCascadeRemove']); + $this->assertFalse($class->fieldMappings['address']['isCascadePersist']); + $this->assertFalse($class->fieldMappings['address']['isCascadeRefresh']); + $this->assertFalse($class->fieldMappings['address']['isCascadeDetach']); + $this->assertFalse($class->fieldMappings['address']['isCascadeMerge']); + + return $class; + } + + /** + * @depends testOwningOneToOneAssocation + * @param ClassMetadata $class + */ + public function testLifecycleCallbacks($class) + { + $this->assertEquals(count($class->lifecycleCallbacks), 2); + $this->assertEquals($class->lifecycleCallbacks['prePersist'][0], 'doStuffOnPrePersist'); + $this->assertEquals($class->lifecycleCallbacks['postPersist'][0], 'doStuffOnPostPersist'); + + return $class; + } + + /** + * @depends testLifecycleCallbacks + * @param ClassMetadata $class + */ + public function testLifecycleCallbacksSupportMultipleMethodNames($class) + { + $this->assertEquals(count($class->lifecycleCallbacks['prePersist']), 2); + $this->assertEquals($class->lifecycleCallbacks['prePersist'][1], 'doOtherStuffOnPrePersistToo'); + + return $class; + } + + /** + * @depends testLifecycleCallbacksSupportMultipleMethodNames + * @param ClassMetadata $class + */ + public function testCustomFieldName($class) + { + $this->assertEquals('name', $class->fieldMappings['name']['fieldName']); + $this->assertEquals('username', $class->fieldMappings['name']['name']); + + return $class; + } + + /** + * @depends testCustomFieldName + * @param ClassMetadata $class + */ + public function testCustomReferenceFieldName($class) + { + $this->assertEquals('morePhoneNumbers', $class->fieldMappings['morePhoneNumbers']['fieldName']); + $this->assertEquals('more_phone_numbers', $class->fieldMappings['morePhoneNumbers']['name']); + + return $class; + } + + /** + * @depends testCustomReferenceFieldName + * @param ClassMetadata $class + */ + public function testCustomEmbedFieldName($class) + { + $this->assertEquals('embeddedPhonenumber', $class->fieldMappings['embeddedPhonenumber']['fieldName']); + $this->assertEquals('embedded_phone_number', $class->fieldMappings['embeddedPhonenumber']['name']); + + return $class; + } + + /** + * @depends testCustomEmbedFieldName + * @param ClassMetadata $class + */ + public function testDiscriminator($class) + { + $this->assertTrue(isset($class->discriminatorField)); + $this->assertTrue(isset($class->discriminatorMap)); + $this->assertEquals(array( + 'fieldName' => 'discr', + 'name' => 'discr', + ), $class->discriminatorField); + $this->assertEquals(array( + 'default' => 'Doctrine\ODM\MongoDB\Tests\Mapping\User', + ), $class->discriminatorMap); + + return $class; + } + + /** + * @depends testDiscriminator + * @param ClassMetadata $class + */ + public function testEmbedDiscriminator($class) + { + $this->assertTrue(isset($class->fieldMappings['otherPhonenumbers']['discriminatorField'])); + $this->assertTrue(isset($class->fieldMappings['otherPhonenumbers']['discriminatorMap'])); + $this->assertEquals('discr', $class->fieldMappings['otherPhonenumbers']['discriminatorField']); + $this->assertEquals(array( + 'home' => 'Doctrine\ODM\MongoDB\Tests\Mapping\HomePhonenumber', + 'work' => 'Doctrine\ODM\MongoDB\Tests\Mapping\WorkPhonenumber' + ), $class->fieldMappings['otherPhonenumbers']['discriminatorMap']); + + return $class; + } + + /** + * @depends testEmbedDiscriminator + * @param ClassMetadata $class + */ + public function testReferenceDiscriminator($class) + { + $this->assertTrue(isset($class->fieldMappings['phonenumbers']['discriminatorField'])); + $this->assertTrue(isset($class->fieldMappings['phonenumbers']['discriminatorMap'])); + $this->assertEquals('discr', $class->fieldMappings['phonenumbers']['discriminatorField']); + $this->assertEquals(array( + 'home' => 'Doctrine\ODM\MongoDB\Tests\Mapping\HomePhonenumber', + 'work' => 'Doctrine\ODM\MongoDB\Tests\Mapping\WorkPhonenumber' + ), $class->fieldMappings['phonenumbers']['discriminatorMap']); + + return $class; + } + + /** + * @depends testCustomFieldName + * @param ClassMetadata $class + */ + public function testIndexes($class) + { + $this->assertTrue(isset($class->indexes[0]['keys']['username'])); + $this->assertEquals(-1, $class->indexes[0]['keys']['username']); + $this->assertTrue(isset($class->indexes[0]['options']['unique'])); + + $this->assertTrue(isset($class->indexes[1]['keys']['email'])); + $this->assertEquals(-1, $class->indexes[1]['keys']['email']); + $this->assertTrue( ! empty($class->indexes[1]['options'])); + $this->assertTrue(isset($class->indexes[1]['options']['unique'])); + $this->assertEquals(true, $class->indexes[1]['options']['unique']); + $this->assertTrue(isset($class->indexes[1]['options']['dropDups'])); + $this->assertEquals(true, $class->indexes[1]['options']['dropDups']); + + $this->assertTrue(isset($class->indexes[2]['keys']['mysqlProfileId'])); + $this->assertEquals(-1, $class->indexes[2]['keys']['mysqlProfileId']); + $this->assertTrue( ! empty($class->indexes[2]['options'])); + $this->assertTrue(isset($class->indexes[2]['options']['unique'])); + $this->assertEquals(true, $class->indexes[2]['options']['unique']); + $this->assertTrue(isset($class->indexes[2]['options']['dropDups'])); + $this->assertEquals(true, $class->indexes[2]['options']['dropDups']); + + return $class; + } +} + +/** + * @ODM\Document(collection="cms_users") + * @ODM\DiscriminatorField(fieldName="discr") + * @ODM\DiscriminatorMap({"default"="Doctrine\ODM\MongoDB\Tests\Mapping\User"}) + */ +class User +{ + /** + * @ODM\Id + */ + public $id; + + /** + * @ODM\String(name="username") + * @ODM\Index(order="desc") + */ + public $name; + + /** + * @ODM\String + * @ODM\UniqueIndex(order="desc", dropDups=true) + */ + public $email; + + /** + * @ODM\Int + * @ODM\UniqueIndex(order="desc", dropDups=true) + */ + public $mysqlProfileId; + + /** + * @ODM\ReferenceOne(targetDocument="Address", cascade={"remove"}) + */ + public $address; + + /** + * @ODM\ReferenceMany(targetDocument="Phonenumber", cascade={"persist"}, discriminatorField="discr", discriminatorMap={"home"="HomePhonenumber", "work"="WorkPhonenumber"}) + */ + public $phonenumbers; + + /** + * @ODM\ReferenceMany(targetDocument="Group", cascade={"all"}) + */ + public $groups; + + /** + * @ODM\ReferenceMany(targetDocument="Phonenumber", name="more_phone_numbers") + */ + public $morePhoneNumbers; + + /** + * @ODM\EmbedMany(targetDocument="Phonenumber", name="embedded_phone_number") + */ + public $embeddedPhonenumber; + + /** + * @ODM\EmbedMany(targetDocument="Phonenumber", discriminatorField="discr", discriminatorMap={"home"="HomePhonenumber", "work"="WorkPhonenumber"}) + */ + public $otherPhonenumbers; + + /** + * @ODM\PrePersist + */ + public function doStuffOnPrePersist() + { + } + + /** + * @ODM\PrePersist + */ + public function doOtherStuffOnPrePersistToo() + { + } + + /** + * @ODM\PostPersist + */ + public function doStuffOnPostPersist() + { + } + + public static function loadMetadata(ClassMetadata $metadata) + { + $metadata->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_NONE); + $metadata->setCollection('cms_users'); + $metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist'); + $metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist'); + $metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist'); + $metadata->setDiscriminatorField(array( + 'fieldName' => 'discr', + )); + $metadata->setDiscriminatorMap(array( + 'default' => __CLASS__, + )); + $metadata->mapField(array( + 'id' => true, + 'fieldName' => 'id', + )); + $metadata->mapField(array( + 'fieldName' => 'name', + 'name' => 'username', + 'type' => 'string', + )); + $metadata->mapField(array( + 'fieldName' => 'email', + 'type' => 'string', + )); + $metadata->mapField(array( + 'fieldName' => 'mysqlProfileId', + 'type' => 'integer', + )); + $metadata->mapOneReference(array( + 'fieldName' => 'address', + 'targetDocument' => 'Doctrine\\ODM\\MongoDB\\Tests\\Mapping\\Address', + 'cascade' => array(0 => 'remove'), + )); + $metadata->mapManyReference(array( + 'fieldName' => 'phonenumbers', + 'targetDocument' => 'Doctrine\\ODM\\MongoDB\\Tests\\Mapping\\Phonenumber', + 'cascade' => array(1 => 'persist'), + 'discriminatorField' => 'discr', + 'discriminatorMap' => array( + 'home' => 'HomePhonenumber', + 'work' => 'WorkPhonenumber' + ), + )); + $metadata->mapManyReference(array( + 'fieldName' => 'morePhoneNumbers', + 'name' => 'more_phone_numbers', + 'targetDocument' => 'Doctrine\\ODM\\MongoDB\\Tests\\Mapping\\Phonenumber', + )); + $metadata->mapManyReference(array( + 'fieldName' => 'groups', + 'targetDocument' => 'Doctrine\\ODM\\MongoDB\\Tests\\Mapping\\Group', + 'cascade' => array( + 0 => 'remove', + 1 => 'persist', + 2 => 'refresh', + 3 => 'merge', + 4 => 'detach', + ), + )); + $metadata->mapOneEmbedded(array( + 'fieldName' => 'embeddedPhonenumber', + 'name' => 'embedded_phone_number', + )); + $metadata->mapManyEmbedded(array( + 'fieldName' => 'otherPhonenumbers', + 'targetDocument' => 'Doctrine\\ODM\\MongoDB\\Tests\\Mapping\\Phonenumber', + 'discriminatorField' => 'discr', + 'discriminatorMap' => array( + 'home' => 'HomePhonenumber', + 'work' => 'WorkPhonenumber', + ), + )); + $metadata->addIndex(array('username' => 'desc'), array('unique' => true)); + $metadata->addIndex(array('email' => 'desc'), array('unique' => true, 'dropDups' => true)); + $metadata->addIndex(array('mysqlProfileId' => 'desc'), array('unique' => true, 'dropDups' => true)); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AnnotationDriverTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AnnotationDriverTest.php new file mode 100644 index 00000000..55f1dcf9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AnnotationDriverTest.php @@ -0,0 +1,119 @@ +setExpectedException('Doctrine\ODM\MongoDB\Mapping\MappingException'); + $annotationDriver->loadMetadataForClass('stdClass', $cm); + } + + /** + * @group DDC-268 + */ + public function testColumnWithMissingTypeDefaultsToString() + { + $cm = new ClassMetadata('Doctrine\ODM\MongoDB\Tests\Mapping\ColumnWithoutType'); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $annotationDriver = new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader); + + $annotationDriver->loadMetadataForClass('Doctrine\ODM\MongoDB\Tests\Mapping\InvalidColumn', $cm); + $this->assertEquals('id', $cm->fieldMappings['id']['type']); + } + + /** + * @group DDC-318 + */ + public function testGetAllClassNamesIsIdempotent() + { + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $original = $annotationDriver->getAllClassNames(); + + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $afterTestReset = $annotationDriver->getAllClassNames(); + + $this->assertEquals($original, $afterTestReset); + } + + /** + * @group DDC-318 + */ + public function testGetAllClassNamesIsIdempotentEvenWithDifferentDriverInstances() + { + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $original = $annotationDriver->getAllClassNames(); + + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $afterTestReset = $annotationDriver->getAllClassNames(); + + $this->assertEquals($original, $afterTestReset); + } + + /** + * @group DDC-318 + */ + public function testGetAllClassNamesReturnsAlreadyLoadedClassesIfAppropriate() + { + $rightClassName = 'Documents\CmsUser'; + $this->_ensureIsLoaded($rightClassName); + + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $classes = $annotationDriver->getAllClassNames(); + + $this->assertContains($rightClassName, $classes); + } + + /** + * @group DDC-318 + */ + public function testGetClassNamesReturnsOnlyTheAppropriateClasses() + { + $extraneousClassName = __NAMESPACE__.'\ColumnWithoutType'; + $this->_ensureIsLoaded($extraneousClassName); + + $annotationDriver = $this->_loadDriverForCMSDocuments(); + $classes = $annotationDriver->getAllClassNames(); + + $this->assertNotContains($extraneousClassName, $classes); + } + + protected function _loadDriverForCMSDocuments() + { + $annotationDriver = $this->_loadDriver(); + $annotationDriver->addPaths(array(__DIR__ . '/../../../../../Documents')); + return $annotationDriver; + } + + protected function _loadDriver() + { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + return new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader); + } + + protected function _ensureIsLoaded($entityClassName) + { + new $entityClassName; + } +} + +/** + * @ODM\Document + */ +class ColumnWithoutType +{ + /** @ODM\Id */ + public $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php new file mode 100644 index 00000000..460ca9e2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php @@ -0,0 +1,112 @@ +factory = new ClassMetadataFactory(); + $this->factory->setDocumentManager($this->dm); + $this->factory->setConfiguration($this->dm->getConfiguration()); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\Mapping\MappingException + */ + public function testGetMetadataForTransientClassThrowsException() + { + $this->factory->getMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\TransientBaseClass'); + } + + public function testGetMetadataForSubclassWithTransientBaseClass() + { + $class = $this->factory->getMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\DocumentSubClass'); + + $this->assertTrue(empty($class->subClasses)); + $this->assertTrue(empty($class->parentClasses)); + $this->assertTrue(isset($class->fieldMappings['id'])); + $this->assertTrue(isset($class->fieldMappings['name'])); + } + + public function testGetMetadataForSubclassWithMappedSuperclass() + { + $class = $this->factory->getMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\DocumentSubClass2'); + + $this->assertTrue(empty($class->subClasses)); + $this->assertTrue(empty($class->parentClasses)); + + $this->assertTrue(isset($class->fieldMappings['mapped1'])); + $this->assertTrue(isset($class->fieldMappings['mapped2'])); + $this->assertTrue(isset($class->fieldMappings['id'])); + $this->assertTrue(isset($class->fieldMappings['name'])); + + $this->assertFalse(isset($class->fieldMappings['mapped1']['inherited'])); + $this->assertFalse(isset($class->fieldMappings['mapped2']['inherited'])); + $this->assertFalse(isset($class->fieldMappings['transient'])); + + $this->assertTrue(isset($class->fieldMappings['mappedRelated1'])); + } + + /** + * @group DDC-388 + */ + public function testSerializationWithPrivateFieldsFromMappedSuperclass() + { + $class = $this->factory->getMetadataFor(__NAMESPACE__ . '\\DocumentSubClass2'); + + $class2 = unserialize(serialize($class)); + + $this->assertTrue(isset($class2->reflFields['mapped1'])); + $this->assertTrue(isset($class2->reflFields['mapped2'])); + $this->assertTrue(isset($class2->reflFields['mappedRelated1'])); + } +} + +class TransientBaseClass +{ + private $transient1; + private $transient2; +} + +/** @ODM\Document */ +class DocumentSubClass extends TransientBaseClass +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; +} + +/** @ODM\MappedSuperclass */ +class MappedSuperclassBase +{ + /** @ODM\String */ + private $mapped1; + /** @ODM\String */ + private $mapped2; + + /** + * @ODM\ReferenceOne(targetDocument="MappedSuperclassRelated1") + */ + private $mappedRelated1; + + private $transient; +} + +/** @ODM\Document */ +class DocumentSubClass2 extends MappedSuperclassBase +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataFactoryTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataFactoryTest.php new file mode 100644 index 00000000..172949a8 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataFactoryTest.php @@ -0,0 +1,121 @@ +_createDocumentManager($mockDriver); + + // Self-made metadata + $cm1 = new ClassMetadata('Doctrine\ODM\MongoDB\Tests\Mapping\TestDocument1'); + $cm1->setCollection('group'); + // Add a mapped field + $cm1->mapField(array('fieldName' => 'name', 'type' => 'string')); + // Add a mapped field + $cm1->mapField(array('fieldName' => 'id', 'id' => true)); + // and a mapped association + $cm1->mapOneEmbedded(array('fieldName' => 'other', 'targetDocument' => 'Other')); + $cm1->mapOneEmbedded(array('fieldName' => 'association', 'targetDocument' => 'Other')); + + // SUT + $cmf = new ClassMetadataFactoryTestSubject(); + $cmf->setDocumentManager($documentManager); + $cmf->setMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\TestDocument1', $cm1); + + // Prechecks + $this->assertEquals(array(), $cm1->parentClasses); + $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm1->inheritanceType); + $this->assertTrue($cm1->hasField('name')); + $this->assertEquals(4, count($cm1->fieldMappings)); + + // Go + $cm1 = $cmf->getMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\TestDocument1'); + + $this->assertEquals('group', $cm1->collection); + $this->assertEquals(array(), $cm1->parentClasses); + $this->assertTrue($cm1->hasField('name')); + } + + public function testHasGetMetadata_NamespaceSeperatorIsNotNormalized() + { + require_once __DIR__."/Documents/GlobalNamespaceDocument.php"; + + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $metadataDriver = new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader); + $metadataDriver->addPaths(array(__DIR__ . '/../../Documents/')); + + $documentManager = $this->_createDocumentManager($metadataDriver); + + $mf = $documentManager->getMetadataFactory(); + $m1 = $mf->getMetadataFor("DoctrineGlobal_Article"); + $h1 = $mf->hasMetadataFor("DoctrineGlobal_Article"); + $h2 = $mf->hasMetadataFor("\DoctrineGlobal_Article"); + $m2 = $mf->getMetadataFor("\DoctrineGlobal_Article"); + + $this->assertNotSame($m1, $m2); + $this->assertFalse($h2); + $this->assertTrue($h1); + } + + protected function _createDocumentManager($metadataDriver) + { + $connMock = new ConnectionMock(); + $config = new \Doctrine\ODM\MongoDB\Configuration(); + + $config->setProxyDir(__DIR__ . '/../../Proxies'); + $config->setProxyNamespace('Doctrine\ODM\MongoDB\Tests\Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../Hydrators'); + $config->setHydratorNamespace('Doctrine\ODM\MongoDB\Tests\Hydrators'); + + $eventManager = new EventManager(); + $mockDriver = new MetadataDriverMock(); + $config->setMetadataDriverImpl($metadataDriver); + + return DocumentManagerMock::create($connMock, $config, $eventManager); + } +} + +/* Test subject class with overriden factory method for mocking purposes */ +class ClassMetadataFactoryTestSubject extends \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory +{ + private $_mockMetadata = array(); + private $_requestedClasses = array(); + + /** @override */ + protected function _newClassMetadataInstance($className) + { + $this->_requestedClasses[] = $className; + if ( ! isset($this->_mockMetadata[$className])) { + throw new InvalidArgumentException("No mock metadata found for class $className."); + } + return $this->_mockMetadata[$className]; + } + + public function setMetadataForClass($className, $metadata) + { + $this->_mockMetadata[$className] = $metadata; + } + + public function getRequestedClasses() + { + return $this->_requestedClasses; + } +} + +class TestDocument1 +{ + private $id; + private $name; + private $other; + private $association; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php new file mode 100644 index 00000000..9594654a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php @@ -0,0 +1,43 @@ +dm->getMetadataFactory(); + $evm = $this->dm->getEventManager(); + $evm->addEventListener(Events::loadClassMetadata, $this); + $classMetadata = $metadataFactory->getMetadataFor('Doctrine\ODM\MongoDB\Tests\Mapping\LoadEventTestDocument'); + $this->assertTrue($classMetadata->hasField('about')); + } + + public function loadClassMetadata(\Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + $field = array( + 'fieldName' => 'about', + 'type' => 'string' + ); + $classMetadata->mapField($field); + } +} + +/** + * @ODM\Document + */ +class LoadEventTestDocument +{ + /** @ODM\Id */ + private $id; + + /** @ODM\String */ + private $name; + + private $about; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php new file mode 100644 index 00000000..36e84f56 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -0,0 +1,190 @@ +assertTrue(count($cm->getReflectionProperties()) == 0); + $this->assertTrue($cm->reflClass instanceof \ReflectionClass); + $this->assertEquals('Documents\CmsUser', $cm->name); + $this->assertEquals('Documents\CmsUser', $cm->rootDocumentName); + $this->assertEquals(array(), $cm->subClasses); + $this->assertEquals(array(), $cm->parentClasses); + $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType); + + // Customize state + $cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION); + $cm->setSubclasses(array("One", "Two", "Three")); + $cm->setParentClasses(array("UserParent")); + $cm->setCustomRepositoryClass("UserRepository"); + $cm->setDiscriminatorField(array('name' => 'disc')); + $cm->mapOneEmbedded(array('fieldName' => 'phonenumbers', 'targetDocument' => 'Bar')); + $cm->setFile('customFileProperty'); + $cm->setDistance('customDistanceProperty'); + $cm->setSlaveOkay(true); + $this->assertTrue(is_array($cm->getFieldMapping('phonenumbers'))); + $this->assertEquals(1, count($cm->fieldMappings)); + + $serialized = serialize($cm); + $cm = unserialize($serialized); + + // Check state + $this->assertTrue(count($cm->getReflectionProperties()) > 0); + $this->assertEquals('Documents', $cm->namespace); + $this->assertTrue($cm->reflClass instanceof \ReflectionClass); + $this->assertEquals('Documents\CmsUser', $cm->name); + $this->assertEquals('UserParent', $cm->rootDocumentName); + $this->assertEquals(array('Documents\One', 'Documents\Two', 'Documents\Three'), $cm->subClasses); + $this->assertEquals(array('UserParent'), $cm->parentClasses); + $this->assertEquals('UserRepository', $cm->customRepositoryClassName); + $this->assertEquals(array('name' => 'disc'), $cm->discriminatorField); + $this->assertTrue(is_array($cm->getFieldMapping('phonenumbers'))); + $this->assertEquals(1, count($cm->fieldMappings)); + $this->assertEquals('customFileProperty', $cm->file); + $this->assertEquals('customDistanceProperty', $cm->distance); + $this->assertTrue($cm->slaveOkay); + $mapping = $cm->getFieldMapping('phonenumbers'); + $this->assertEquals('Documents\Bar', $mapping['targetDocument']); + } + + public function testOwningSideAndInverseSide() + { + $cm = new ClassMetadataInfo('Documents\User'); + $cm->mapManyReference(array('fieldName' => 'articles', 'inversedBy' => 'user')); + $this->assertTrue($cm->fieldMappings['articles']['isOwningSide']); + + $cm = new ClassMetadataInfo('Documents\Article'); + $cm->mapOneReference(array('fieldName' => 'user', 'mappedBy' => 'articles')); + $this->assertTrue($cm->fieldMappings['user']['isInverseSide']); + } + + public function testFieldIsNullable() + { + $cm = new ClassMetadata('Documents\CmsUser'); + + // Explicit Nullable + $cm->mapField(array('fieldName' => 'status', 'nullable' => true, 'type' => 'string', 'length' => 50)); + $this->assertTrue($cm->isNullable('status')); + + // Explicit Not Nullable + $cm->mapField(array('fieldName' => 'username', 'nullable' => false, 'type' => 'string', 'length' => 50)); + $this->assertFalse($cm->isNullable('username')); + + // Implicit Not Nullable + $cm->mapField(array('fieldName' => 'name', 'type' => 'string', 'length' => 50)); + $this->assertFalse($cm->isNullable('name'), "By default a field should not be nullable."); + } + + /** + * @group DDC-115 + */ + public function testMapAssocationInGlobalNamespace() + { + require_once __DIR__."/Documents/GlobalNamespaceDocument.php"; + + $cm = new ClassMetadata('DoctrineGlobal_Article'); + $cm->mapManyEmbedded(array( + 'fieldName' => 'author', + 'targetDocument' => 'DoctrineGlobal_User', + )); + + $this->assertEquals("DoctrineGlobal_User", $cm->fieldMappings['author']['targetDocument']); + } + + public function testMapManyToManyJoinTableDefaults() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $cm->mapManyEmbedded( + array( + 'fieldName' => 'groups', + 'targetDocument' => 'CmsGroup' + )); + + $assoc = $cm->fieldMappings['groups']; + $this->assertTrue(is_array($assoc)); + } + + /** + * @group DDC-115 + */ + public function testSetDiscriminatorMapInGlobalNamespace() + { + require_once __DIR__."/Documents/GlobalNamespaceDocument.php"; + + $cm = new ClassMetadata('DoctrineGlobal_User'); + $cm->setDiscriminatorMap(array('descr' => 'DoctrineGlobal_Article', 'foo' => 'DoctrineGlobal_User')); + + $this->assertEquals("DoctrineGlobal_Article", $cm->discriminatorMap['descr']); + $this->assertEquals("DoctrineGlobal_User", $cm->discriminatorMap['foo']); + } + + /** + * @group DDC-115 + */ + public function testSetSubClassesInGlobalNamespace() + { + require_once __DIR__."/Documents/GlobalNamespaceDocument.php"; + + $cm = new ClassMetadata('DoctrineGlobal_User'); + $cm->setSubclasses(array('DoctrineGlobal_Article')); + + $this->assertEquals("DoctrineGlobal_Article", $cm->subClasses[0]); + } + + public function testDuplicateFieldMapping() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $a1 = array('reference' => true, 'type' => 'many', 'fieldName' => 'foo', 'targetDocument' => 'stdClass'); + $a2 = array('type' => 'string', 'fieldName' => 'foo'); + + $cm->mapField($a1); + $cm->mapField($a2); + + $this->assertEquals('string', $cm->fieldMappings['foo']['type']); + } + + public function testDuplicateColumnName_DiscriminatorColumn_ThrowsMappingException() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $cm->mapField(array('fieldName' => 'name')); + + $this->setExpectedException('Doctrine\ODM\MongoDB\Mapping\MappingException'); + $cm->setDiscriminatorField(array('name' => 'name')); + } + + public function testDuplicateFieldName_DiscriminatorColumn2_ThrowsMappingException() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $cm->setDiscriminatorField(array('name' => 'name')); + + $this->setExpectedException('Doctrine\ODM\MongoDB\Mapping\MappingException'); + $cm->mapField(array('fieldName' => 'name')); + } + + public function testDuplicateFieldAndAssocationMapping1() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $cm->mapField(array('fieldName' => 'name')); + $cm->mapOneEmbedded(array('fieldName' => 'name', 'targetDocument' => 'CmsUser')); + + $this->assertEquals('one', $cm->fieldMappings['name']['type']); + } + + public function testDuplicateFieldAndAssocationMapping2() + { + $cm = new ClassMetadata('Documents\CmsUser'); + $cm->mapOneEmbedded(array('fieldName' => 'name', 'targetDocument' => 'CmsUser')); + $cm->mapField(array('fieldName' => 'name', 'columnName' => 'name', 'type' => 'string')); + + $this->assertEquals('string', $cm->fieldMappings['name']['type']); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php new file mode 100644 index 00000000..b7c5b3ee --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php @@ -0,0 +1,57 @@ + + */ +abstract class AbstractDriverTest extends \PHPUnit_Framework_TestCase +{ + protected $driver; + + public function setUp() + { + // implement driver setup and metadata read + } + + public function tearDown() + { + unset ($this->driver); + } + + public function testDriver() + { + + $classMetadata = new ClassMetadata('TestDocuments\User'); + $this->driver->loadMetadataForClass('TestDocuments\User', $classMetadata); + + $this->assertEquals(array( + 'fieldName' => 'id', + 'id' => true, + 'name' => '_id', + 'type' => 'id', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false + ), $classMetadata->fieldMappings['id']); + + $this->assertEquals(array( + 'fieldName' => 'username', + 'name' => 'username', + 'type' => 'string', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false + ), $classMetadata->fieldMappings['username']); + + $this->assertEquals(array( + 'fieldName' => 'createdAt', + 'name' => 'createdAt', + 'type' => 'date', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false + ), $classMetadata->fieldMappings['createdAt']); + + $this->assertEquals(array( + 'fieldName' => 'tags', + 'name' => 'tags', + 'type' => 'collection', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + ), $classMetadata->fieldMappings['tags']); + + $this->assertEquals(array( + 'association' => 3, + 'fieldName' => 'address', + 'name' => 'address', + 'type' => 'one', + 'embedded' => true, + 'targetDocument' => 'Documents\Address', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + ), $classMetadata->fieldMappings['address']); + + $this->assertEquals(array( + 'association' => 4, + 'fieldName' => 'phonenumbers', + 'name' => 'phonenumbers', + 'type' => 'many', + 'embedded' => true, + 'targetDocument' => 'Documents\Phonenumber', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + ), $classMetadata->fieldMappings['phonenumbers']); + + $this->assertEquals(array( + 'association' => 1, + 'fieldName' => 'profile', + 'name' => 'profile', + 'type' => 'one', + 'reference' => true, + 'simple' => true, + 'targetDocument' => 'Documents\Profile', + 'isCascadeCallbacks' => true, + 'isCascadeDetach' => true, + 'isCascadeMerge' => true, + 'isCascadePersist' => true, + 'isCascadeRefresh' => true, + 'isCascadeRemove' => true, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + 'inversedBy' => null, + 'mappedBy' => null, + 'repositoryMethod' => null, + 'limit' => null, + 'skip' => null, + ), $classMetadata->fieldMappings['profile']); + + $this->assertEquals(array( + 'association' => 1, + 'fieldName' => 'account', + 'name' => 'account', + 'type' => 'one', + 'reference' => true, + 'simple' => false, + 'targetDocument' => 'Documents\Account', + 'isCascadeCallbacks' => true, + 'isCascadeDetach' => true, + 'isCascadeMerge' => true, + 'isCascadePersist' => true, + 'isCascadeRefresh' => true, + 'isCascadeRemove' => true, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + 'inversedBy' => null, + 'mappedBy' => null, + 'repositoryMethod' => null, + 'limit' => null, + 'skip' => null, + ), $classMetadata->fieldMappings['account']); + + $this->assertEquals(array( + 'association' => 2, + 'fieldName' => 'groups', + 'name' => 'groups', + 'type' => 'many', + 'reference' => true, + 'simple' => false, + 'targetDocument' => 'Documents\Group', + 'isCascadeCallbacks' => true, + 'isCascadeDetach' => true, + 'isCascadeMerge' => true, + 'isCascadePersist' => true, + 'isCascadeRefresh' => true, + 'isCascadeRemove' => true, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + 'strategy' => 'pushAll', + 'inversedBy' => null, + 'mappedBy' => null, + 'repositoryMethod' => null, + 'limit' => null, + 'skip' => null, + ), $classMetadata->fieldMappings['groups']); + + $this->assertEquals(array( + 'postPersist' => array('doStuffOnPostPersist', 'doOtherStuffOnPostPersist'), + 'prePersist' => array('doStuffOnPrePersist') + ), + $classMetadata->lifecycleCallbacks + ); + + $classMetadata = new ClassMetadata('TestDocuments\EmbeddedDocument'); + $this->driver->loadMetadataForClass('TestDocuments\EmbeddedDocument', $classMetadata); + + $this->assertEquals(array( + 'fieldName' => 'name', + 'name' => 'name', + 'type' => 'string', + 'isCascadeCallbacks' => false, + 'isCascadeDetach' => false, + 'isCascadeMerge' => false, + 'isCascadePersist' => false, + 'isCascadeRefresh' => false, + 'isCascadeRemove' => false, + 'isInverseSide' => false, + 'isOwningSide' => true, + 'nullable' => false, + ), $classMetadata->fieldMappings['name']); + } + +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php new file mode 100644 index 00000000..0f4c18d2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php @@ -0,0 +1,16 @@ + + */ +class XmlDriverTest extends AbstractDriverTest +{ + public function setUp() + { + $this->driver = new XmlDriver(__DIR__ . '/fixtures/xml'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/YamlDriverTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/YamlDriverTest.php new file mode 100644 index 00000000..68725581 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/YamlDriverTest.php @@ -0,0 +1,20 @@ + + */ +class YamlDriverTest extends AbstractDriverTest +{ + public function setUp() + { + if (!class_exists('Symfony\Component\Yaml\Yaml', true)) { + $this->markTestSkipped('This test requires the Symfony YAML component'); + } + + $this->driver = new YamlDriver(__DIR__ . '/fixtures/yaml'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/EmbeddedDocument.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/EmbeddedDocument.php new file mode 100644 index 00000000..62021cf2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/EmbeddedDocument.php @@ -0,0 +1,10 @@ +phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groups = array(); + $this->createdAt = new \DateTime(); + } + + public function getId() + { + return $this->id; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } + + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + } + + public function getCreatedAt() + { + return $this->createdAt; + } + + public function getAddress() + { + return $this->address; + } + + public function setAddress(Address $address) + { + $this->address = $address; + } + + public function setProfile(Profile $profile) + { + $this->profile = $profile; + } + + public function getProfile() + { + return $this->profile; + } + + public function setAccount(Account $account) + { + $this->account = $account; + } + + public function getAccount() + { + return $this->account; + } + + public function getPhonenumbers() + { + return $this->phonenumbers; + } + + public function addPhonenumber(Phonenumber $phonenumber) + { + $this->phonenumbers[] = $phonenumber; + } + + public function getGroups() + { + return $this->groups; + } + + public function addGroup(Group $group) + { + $this->groups[] = $group; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.EmbeddedDocument.dcm.xml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.EmbeddedDocument.dcm.xml new file mode 100644 index 00000000..a0f76665 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.EmbeddedDocument.dcm.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.User.dcm.xml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.User.dcm.xml new file mode 100644 index 00000000..1c5503a4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/xml/TestDocuments.User.dcm.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.EmbeddedDocument.dcm.yml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.EmbeddedDocument.dcm.yml new file mode 100644 index 00000000..76bbd9ed --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.EmbeddedDocument.dcm.yml @@ -0,0 +1,4 @@ +TestDocuments\EmbeddedDocument: + type: embeddedDocument + fields: + name: string \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.User.dcm.yml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.User.dcm.yml new file mode 100644 index 00000000..144372f4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/fixtures/yaml/TestDocuments.User.dcm.yml @@ -0,0 +1,38 @@ +TestDocuments\User: + db: documents + collection: user + fields: + id: + fieldName: id + id: true + username: + fieldName: username + type: string + createdAt: + fieldName: createdAt + type: date + tags: + type: collection + embedOne: + address: + targetDocument: Documents\Address + test: + targetDocument: TestDocuments\EmbeddedDocument + embedMany: + phonenumbers: + targetDocument: Documents\Phonenumber + referenceOne: + profile: + simple: true + targetDocument: Documents\Profile + cascade: all + account: + targetDocument: Documents\Account + cascade: all + referenceMany: + groups: + targetDocument: Documents\Group + cascade: all + lifecycleCallbacks: + prePersist: [doStuffOnPrePersist] + postPersist: [doStuffOnPostPersist, doOtherStuffOnPostPersist] diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php new file mode 100644 index 00000000..e92cc67a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php @@ -0,0 +1,13 @@ +markTestSkipped('This test requires the Symfony YAML component'); + } + + return new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); + } + + public function testAlternateRelationshipMappingSyntaxShouldSetDefaults() + { + $className = __NAMESPACE__.'\AlternateUser'; + $mappingDriver = new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + + foreach (array('address', 'phonenumbers') as $referencedField) { + foreach (array('inversedBy', 'limit', 'mappedBy', 'repositoryMethod', 'simple', 'skip', 'strategy', 'targetDocument') as $key) { + $this->assertArrayHasKey($key, $class->fieldMappings[$referencedField]); + } + } + + foreach (array('embeddedPhonenumber', 'otherPhonenumbers') as $embeddedField) { + foreach (array('strategy', 'targetDocument') as $key) { + $this->assertArrayHasKey($key, $class->fieldMappings[$embeddedField]); + } + } + } + + public function testFieldLevelIndexSyntaxWithBooleanValues() + { + $className = __NAMESPACE__.'\AlternateUser'; + $mappingDriver = new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); + + $class = new ClassMetadata($className); + $mappingDriver->loadMetadataForClass($className, $class); + + $this->assertEquals(1, $class->indexes[0]['keys']['username']); + $this->assertTrue($class->indexes[0]['options']['unique']); + $this->assertFalse(isset($class->indexes[0]['options']['sparse'])); + + $this->assertEquals(1, $class->indexes[1]['keys']['firstName']); + $this->assertFalse(isset($class->indexes[1]['options']['unique'])); + $this->assertFalse(isset($class->indexes[1]['options']['sparse'])); + + $this->assertEquals(1, $class->indexes[2]['keys']['middleName']); + $this->assertFalse(isset($class->indexes[2]['options']['unique'])); + $this->assertTrue($class->indexes[2]['options']['sparse']); + } +} + +class AlternateUser +{ + public $id; + public $username; + public $firstName; + public $middleName; + public $address; + public $phonenumbers; + public $embeddedPhonenumber; + public $otherPhonenumbers; +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.xml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.xml new file mode 100644 index 00000000..c4e6abca --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.AlternateUser.dcm.yml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.AlternateUser.dcm.yml new file mode 100644 index 00000000..0145502e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.AlternateUser.dcm.yml @@ -0,0 +1,29 @@ +# This tests alternate syntax for defining embed/reference relations +Doctrine\ODM\MongoDB\Tests\Mapping\AlternateUser: + type: document + collection: cms_alternate_users + fields: + id: + type: id + id: true + username: + type: string + unique: true + firstName: + type: string + index: true + middleName: + type: string + sparse: true + address: + reference: true + type: one + phonenumbers: + reference: true + type: many + embeddedPhonenumber: + embedded: true + type: one + otherPhonenumbers: + embedded: true + type: many diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.yml b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.yml new file mode 100644 index 00000000..63ea2c22 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mapping/yaml/Doctrine.ODM.MongoDB.Tests.Mapping.User.dcm.yml @@ -0,0 +1,66 @@ +Doctrine\ODM\MongoDB\Tests\Mapping\User: + type: document + collection: cms_users + discriminatorField: + fieldName: discr + discriminatorMap: + default: Doctrine\ODM\MongoDB\Tests\Mapping\User + fields: + id: + type: id + id: true + name: + type: string + name: username + email: + type: string + mysqlProfileId: + type: integer + unique: + dropDups: true + order: desc + indexes: + index1: + keys: + username: desc + options: + unique: true + index2: + keys: + email: desc + options: + unique: true + dropDups: true + referenceOne: + address: + targetDocument: Address + cascade: [ remove ] + referenceMany: + phonenumbers: + targetDocument: Phonenumber + cascade: [ persist ] + discriminatorField: discr + discriminatorMap: + home: HomePhonenumber + work: WorkPhonenumber + groups: + targetDocument: Group + cascade: [ all ] + morePhoneNumbers: + targetDocument: Phonenumber + name: more_phone_numbers + embedOne: + embeddedPhonenumber: + targetDocument: Phonenumber + name: embedded_phone_number + embedMany: + otherPhonenumbers: + targetDocument: Phonenumber + discriminatorField: discr + discriminatorMap: + home: HomePhonenumber + work: WorkPhonenumber + + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/ClassMetadataMock.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/ClassMetadataMock.php new file mode 100644 index 00000000..02327584 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/ClassMetadataMock.php @@ -0,0 +1,7 @@ +uowMock) ? $this->uowMock : parent::getUnitOfWork(); + } + + public function setUnitOfWork(UnitOfWork $uow) + { + $this->uowMock = $uow; + } + + public function setProxyFactory(ProxyFactory $proxyFactory) + { + $this->proxyFactoryMock = $proxyFactory; + } + + public function getProxyFactory() + { + return isset($this->proxyFactoryMock) ? $this->proxyFactoryMock : parent::getProxyFactory(); + } + + public function setMetadataFactory(ClassMetadataFactory $metadataFactory) + { + $this->metadataFactory = $metadataFactory; + } + + public function getMetadataFactory() + { + return isset($this->metadataFactory) ? $this->metadataFactory : parent::getMetadataFactory(); + } + + public function setSchemaManager(SchemaManager $schemaManager) + { + $this->schemaManager = $schemaManager; + } + + public function getSchemaManager() + { + return isset($this->schemaManager) ? $this->schemaManager : parent::getSchemaManager(); + } + + public function setDocumentCollection($documentName, Collection $collection) + { + $this->documentCollections[$documentName] = $collection; + } + + public function getDocumentCollection($documentName) + { + return isset($this->documentCollections[$documentName]) ? $this->documentCollections[$documentName] : parent::getDocumentCollection($documentName); + } + + public function setDocumentDatabase($documentName, Database $database) + { + $this->documentDatabases[$documentName] = $database; + } + + public function getDocumentDatabase($documentName) + { + return isset($this->documentDatabases[$documentName]) ? $this->documentDatabases[$documentName] : parent::getDocumentDatabase($documentName); + } + + + public function setClassMetadata($documentName, ClassMetadata $metadata) + { + $this->documentMetadatas[$documentName] = $metadata; + } + + public function getClassMetadata($documentName) + { + return isset($this->documentMetadatas[$documentName]) ? $this->documentMetadatas[$documentName] : parent::getClassMetadata($documentName); + } + + public static function create(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null) + { + if (is_null($config)) { + $config = new \Doctrine\ODM\MongoDB\Configuration(); + $config->setProxyDir(__DIR__ . '/../Proxies'); + $config->setProxyNamespace('Doctrine\Tests\Proxies'); + + $config->setHydratorDir(__DIR__ . '/../Hydrators'); + $config->setHydratorNamespace('Doctrine\Tests\Hydrators'); + + $config->setMetadataDriverImpl(\Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver::create()); + } + if (is_null($eventManager)) { + $eventManager = new \Doctrine\Common\EventManager(); + } + return new DocumentManagerMock($conn, $config, $eventManager); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/DocumentPersisterMock.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/DocumentPersisterMock.php new file mode 100644 index 00000000..c3ec364d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/DocumentPersisterMock.php @@ -0,0 +1,87 @@ + $document + * @return + * @override + */ + public function insert($document, array $options = array()) + { + $this->inserts[] = $document; + $id = $this->identityColumnValueCounter++; + $this->postInsertIds[$id] = array($id, $document); + return $id; + } + + public function addInsert($document) + { + $this->inserts[] = $document; + $id = $this->identityColumnValueCounter++; + $this->postInsertIds[$id] = array($id, $document); + return $id; + } + + public function executeInserts(array $options = array()) + { + return $this->postInsertIds; + } + + public function update($document, array $options = array()) + { + $this->updates[] = $document; + } + + public function exists($document) + { + $this->existsCalled = true; + } + + public function delete($document, array $options = array()) + { + $this->deletes[] = $document; + } + + public function getInserts() + { + return $this->inserts; + } + + public function getUpdates() + { + return $this->updates; + } + + public function getDeletes() + { + return $this->deletes; + } + + public function reset() + { + $this->existsCalled = false; + $this->identityColumnValueCounter = 0; + $this->inserts = array(); + $this->updates = array(); + $this->deletes = array(); + } + + public function isExistsCalled() + { + return $this->existsCalled; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/MetadataDriverMock.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/MetadataDriverMock.php new file mode 100644 index 00000000..3d918e17 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/MetadataDriverMock.php @@ -0,0 +1,24 @@ +getDocumentManager()->getUnitOfWork(); + foreach ($uow->getScheduledDocumentUpdates() as $document) { + $uow->clearDocumentChangeSet(spl_object_hash($document)); + } + } + + public function preUpdate(PreUpdateEventArgs $args) + { + return; // fatal error + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/UnitOfWorkMock.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/UnitOfWorkMock.php new file mode 100644 index 00000000..8cba211b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Mocks/UnitOfWorkMock.php @@ -0,0 +1,58 @@ +_persisterMock[$documentName]) ? + $this->_persisterMock[$documentName] : parent::getDocumentPersister($documentName); + } + + /** + * @param $document + * @override + */ + public function getDocumentChangeSet($document) + { + $oid = spl_object_hash($document); + return isset($this->_mockDataChangeSets[$oid]) ? + $this->_mockDataChangeSets[$oid] : parent::getDocumentChangeSet($document); + } + + /* MOCK API */ + + /** + * Sets a (mock) persister for an document class that will be returned when + * getDocumentPersister() is invoked for that class. + * + * @param $documentName + * @param $persister + */ + public function setDocumentPersister($documentName, $persister) + { + $this->_persisterMock[$documentName] = $persister; + } + + public function setDataChangeSet($document, array $mockChangeSet) + { + $this->_mockDataChangeSets[spl_object_hash($document)] = $mockChangeSet; + } + + public function setDocumentState($document, $state) + { + $this->_documentStates[spl_object_hash($document)] = $state; + } + + public function setOriginalDocumentData($document, array $originalData) + { + $this->_originalDocumentData[spl_object_hash($document)] = $originalData; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/MongoCollectionTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/MongoCollectionTest.php new file mode 100644 index 00000000..1c12fab6 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/MongoCollectionTest.php @@ -0,0 +1,15 @@ + + */ +class MongoCollectionTest extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + public function testGridFSEmptyResult() + { + $mongoCollection = $this->dm->getDocumentCollection('Documents\File'); + $this->assertNull($mongoCollection->findOne(array('_id' => 'definitelynotanid'))); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollectionTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollectionTest.php new file mode 100644 index 00000000..fbe97978 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollectionTest.php @@ -0,0 +1,50 @@ + + */ +class PersistentCollectionTest extends \PHPUnit_Framework_TestCase +{ + public function testSlice() + { + list ($start, $limit) = array(0, 25); + $collection = $this->getMockCollection(); + $collection->expects($this->once()) + ->method('slice') + ->with($start, $limit) + ->will($this->returnValue(true)); + $dm = $this->getMockDocumentManager(); + $uow = $this->getMockUnitOfWork(); + $pCollection = new PersistentCollection($collection, $dm, $uow, '$'); + $pCollection->slice($start, $limit); + } + + + /** + * @return Doctrine\ODM\MongoDB\DocumentManager + */ + private function getMockDocumentManager() + { + return $this->getMock('Doctrine\ODM\MongoDB\DocumentManager', array(), array(), '', false, false); + } + + /** + * @return Doctrine\ODM\MongoDB\UnitOfWork + */ + private function getMockUnitOfWork() + { + return $this->getMock('Doctrine\ODM\MongoDB\UnitOfWork', array(), array(), '', false, false); + } + + /** + * @return Doctrine\Common\Collections\Collection + */ + private function getMockCollection() + { + return $this->getMock('Doctrine\Common\Collections\Collection', get_class_methods('Doctrine\Common\Collections\Collection'), array(), '', false, false); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DereferenceTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DereferenceTest.php new file mode 100644 index 00000000..1e39d0ea --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DereferenceTest.php @@ -0,0 +1,66 @@ +pb = $this->dm->getUnitOfWork()->getPersistenceBuilder(); + } + + public function tearDown() + { + unset($this->pb); + parent::tearDown(); + } + + public function testDereferenceManyWithSetStrategyDoesNotUnsetFirst() + { + $post = new Post(); + $post->title = 'test'; + $this->dm->persist($post); + $this->dm->flush(); + $this->dm->clear(); + + $post = $this->dm->find(get_class($post), $post->id); + + $comment = new Comment(); + $comment->subject = 'test'; + + $post->comments = array(); + $post->comments[] = $comment; + + $this->dm->flush(); + + } +} + +/** @ODM\Document */ +class Post +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $title; + + /** @ODM\EmbedMany(targetDocument="Comment", strategy="set") */ + public $comments = array(); +} + +/** @ODM\EmbeddedDocument */ +class Comment +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $subject; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php new file mode 100644 index 00000000..5c14368b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php @@ -0,0 +1,299 @@ +pb = $this->dm->getUnitOfWork()->getPersistenceBuilder(); + } + + public function tearDown() + { + unset($this->pb); + parent::tearDown(); + } + + public function testQueryBuilderUpdateWithDiscriminatorMap() + { + $testCollection = new SameCollection1(); + $id = '4f28aa84acee413889000001'; + + $testCollection->id = $id; + $testCollection->name = 'First entry'; + $this->dm->persist($testCollection); + $this->dm->flush(); + $this->uow->computeChangeSets(); + + /** + * @var \Doctrine\ODM\MongoDB\Query\Builder $qb + */ + $qb = $this->dm->createQueryBuilder('Documents\Functional\SameCollection1'); + $qb->update() + ->field('ok')->set(true) + ->field('test')->set('OK! TEST') + ->field('id')->equals($id); + + /** + * @var \Doctrine\ODM\MongoDB\Query\Query $query + * @var \Doctrine\ODM\MongoDB\Cursor $results + */ + $query = $qb->getQuery(); + $results = $query->execute(); + + $this->dm->refresh($testCollection); + + $this->assertEquals('OK! TEST', $testCollection->test); + } + + public function testFindWithOrOnCollectionWithDiscriminatorMap() + { + $sameCollection1 = new SameCollection1(); + $sameCollection2a = new SameCollection2(); + $sameCollection2b = new SameCollection2(); + $ids = array( + '4f28aa84acee413889000001', + '4f28aa84acee41388900002a', + '4f28aa84acee41388900002b' + ); + + $sameCollection1->id = $ids[0]; + $sameCollection1->name = 'First entry in SameCollection1'; + $sameCollection1->test = 1; + $this->dm->persist($sameCollection1); + $this->dm->flush(); + + $sameCollection2a->id = $ids[1]; + $sameCollection2a->name = 'First entry in SameCollection2'; + $sameCollection2a->ok = true; + $this->dm->persist($sameCollection2a); + $this->dm->flush(); + + $sameCollection2b->id = $ids[2]; + $sameCollection2b->name = 'Second entry in SameCollection2'; + $sameCollection2b->w00t = '!!'; + $this->dm->persist($sameCollection2b); + $this->dm->flush(); + + $this->uow->computeChangeSets(); + + /** + * @var \Doctrine\ODM\MongoDB\Query\Builder $qb + */ + $qb = $this->dm->createQueryBuilder('Documents\Functional\SameCollection2'); + $qb + ->field('id')->in($ids) + ->select('id')->hydrate(false); + /** + * @var \Doctrine\ODM\MongoDB\Query\Query $query + * @var \Doctrine\ODM\MongoDB\Cursor $results + */ + $query = $qb->getQuery(); + $debug = $query->debug(); + $results = $query->execute(); + + $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $results); + + $targetClass = $this->dm->getClassMetadata('Documents\Functional\SameCollection2'); + $mongoId1 = $targetClass->getDatabaseIdentifierValue($ids[1]); + + $this->assertEquals($mongoId1, $debug['_id']['$in'][1]); + + + $this->assertEquals(2, $results->count(true)); + } + + public function testPrepareUpdateDataDoesNotIncludeId() + { + $article = new CmsArticle(); + $article->topic = 'persistence builder test'; + $this->dm->persist($article); + $this->dm->flush(); + $this->dm->clear(); + + $article = $this->dm->getRepository(get_class($article))->find($article->id); + $article->id = null; + $article->topic = 'test'; + + $this->uow->computeChangeSets(); + $data = $this->pb->prepareUpdateData($article); + $this->assertFalse(isset($data['$unset']['_id'])); + } + + public function testPrepareInsertDataWithCreatedReferenceOne() + { + $article = new CmsArticle(); + $article->title = 'persistence builder test'; + $this->dm->persist($article); + $this->dm->flush(); + $comment = new CmsComment(); + $comment->article = $article; + + $this->dm->persist($comment); + $this->uow->computeChangeSets(); + + $expectedData = array( + 'article' => array( + '$db' => 'doctrine_odm_tests', + '$id' => new \MongoId($article->id), + '$ref' => 'CmsArticle' + ) + ); + $this->assertDocumentInsertData($expectedData, $this->pb->prepareInsertData($comment)); + } + + public function testPrepareInsertDataWithFetchedReferenceOne() + { + $article = new CmsArticle(); + $article->title = 'persistence builder test'; + $this->dm->persist($article); + $this->dm->flush(); + $this->dm->clear(); + + $article = $this->dm->find(get_class($article), $article->id); + $comment = new CmsComment(); + $comment->article = $article; + + $this->dm->persist($comment); + $this->uow->computeChangeSets(); + + $expectedData = array( + 'article' => array( + '$db' => 'doctrine_odm_tests', + '$id' => new \MongoId($article->id), + '$ref' => 'CmsArticle' + ) + ); + $this->assertDocumentInsertData($expectedData, $this->pb->prepareInsertData($comment)); + } + + public function testPrepareUpsertData() + { + $article = new CmsArticle(); + $article->title = 'persistence builder test'; + $this->dm->persist($article); + $this->dm->flush(); + $this->dm->clear(); + + $article = $this->dm->find(get_class($article), $article->id); + $comment = new CmsComment(); + $comment->topic = 'test'; + $comment->text = 'text'; + $comment->article = $article; + + $this->dm->persist($comment); + $this->uow->computeChangeSets(); + + $expectedData = array( + '$set' => array( + 'topic' => 'test', + 'text' => 'text', + 'article' => array( + '$db' => 'doctrine_odm_tests', + '$id' => new \MongoId($article->id), + '$ref' => 'CmsArticle' + ) + ) + ); + $this->assertEquals($expectedData, $this->pb->prepareUpsertData($comment)); + } + + /** + * @dataProvider getDocumentsAndExpectedData + */ + public function testPrepareInsertData($document, array $expectedData) + { + $this->dm->persist($document); + $this->uow->computeChangeSets(); + $this->assertDocumentInsertData($expectedData, $this->pb->prepareInsertData($document)); + } + + /** + * Provides data for @see PersistenceBuilderTest::testPrepareInsertData() + * Returns arrays of array(document => expected data) + * + * @return array + */ + public function getDocumentsAndExpectedData() + { + return array( + array(new ConfigurableProduct('Test Product'), array('name' => 'Test Product')), + array(new Currency('USD', 1), array('name' => 'USD', 'multiplier' => 1)), + ); + } + + private function assertDocumentInsertData(array $expectedData, array $preparedData = null) + { + foreach ($preparedData as $key => $value) { + if ($key === '_id') { + $this->assertInstanceOf('MongoId', $value); + continue; + } + $this->assertEquals($expectedData[$key], $value); + } + if ( ! isset($preparedData['_id'])) { + $this->fail('insert data should always contain id'); + } + unset($preparedData['_id']); + $this->assertEquals(array_keys($expectedData), array_keys($preparedData)); + } + + public function testAdvancedQueriesOnReferenceWithDiscriminatorMap() + { + $article = new CmsArticle(); + $article->id = '4f8373f952fbfe7411000001'; + $article->title = 'advanced queries test'; + $this->dm->persist($article); + $this->dm->flush(); + + $comment = new CmsComment(); + $comment->id = '4f8373f952fbfe7411000002'; + $comment->article = $article; + $this->dm->persist($comment); + $this->dm->flush(); + + $this->uow->computeChangeSets(); + + $articleId = (string) $article->id; + $commentId = (string) $comment->id; + + /** + * @var \Doctrine\ODM\MongoDB\Query\Builder $qb + */ + $qb = $this->dm->createQueryBuilder('Documents\CmsComment'); + $qb + ->field('article.id')->in(array($articleId)); + /** + * @var \Doctrine\ODM\MongoDB\Query\Query $query + * @var \Doctrine\ODM\MongoDB\Cursor $results + */ + $query = $qb->getQuery(); + $debug = $query->debug(); + $results = $query->execute(); + + $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $results); + + $singleResult = $results->getSingleResult(); + $this->assertInstanceOf('Documents\CmsComment', $singleResult); + + $this->assertEquals($commentId, $singleResult->id); + + $this->assertInstanceOf('Documents\CmsArticle', $singleResult->article); + + $this->assertEquals($articleId, $singleResult->article->id); + $this->assertEquals(1, $results->count(true)); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/ExprTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/ExprTest.php new file mode 100644 index 00000000..4a4434c5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/ExprTest.php @@ -0,0 +1,222 @@ +dm->createQueryBuilder('Documents\User') + ->field('groups.id')->in($ids) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['groups.$id']['$in'])); + } + + public function testAllIsPrepared() + { + $id = '4f28aa84acee41388900000a'; + $ids = array($id); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups.id')->all($ids) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['groups.$id']['$all'])); + } + + public function testNotEqualIsPrepared() + { + $id = '4f28aa84acee41388900000a'; + $ids = array($id); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups.id')->notEqual($ids) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['groups.$id']['$ne'])); + } + + public function testNotInIsPrepared() + { + $id = '4f28aa84acee41388900000a'; + $ids = array($id); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('groups.id')->notIn($ids) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['groups.$id']['$nin'])); + } + + public function testAndIsPrepared() + { + $id = '4f28aa84acee41388900000a'; + $ids = array($id); + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $qb + ->addAnd($qb->expr()->field('groups.id')->in($ids)) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['$and'][0]['groups.$id']['$in'])); + } + + public function testOrIsPrepared() + { + $id = '4f28aa84acee41388900000a'; + $ids = array($id); + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $qb + ->addOr($qb->expr()->field('groups.id')->in($ids)) + ->select('id')->hydrate(false); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals($id, (string) current($debug['$or'][0]['groups.$id']['$in'])); + } + + public function testPrepareNestedDocuments() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('address.subAddress.subAddress.subAddress.test')->equals('test'); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals(array('address.subAddress.subAddress.subAddress.testFieldName' => 'test'), $debug); + } + + public function testPrepareNestedCollectionDocuments() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('phonenumbers.$.phonenumber')->equals('test'); + $query = $qb->getQuery(); + $debug = $query->debug(); + $this->assertEquals(array('phonenumbers.$.phonenumber' => 'test'), $debug); + } + + public function testSortIsPrepared() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->sort('id', 'desc'); + $query = $qb->getQuery(); + $query = $query->getQuery(); + $this->assertEquals(array('_id' => -1), $query['sort']); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->sort('address.subAddress.subAddress.subAddress.test', 'asc'); + $query = $qb->getQuery(); + $query = $query->getQuery(); + $this->assertEquals(array('address.subAddress.subAddress.subAddress.testFieldName' => 1), $query['sort']); + } + + public function testNestedWithOperator() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('address.subAddress.subAddress.subAddress.test')->notIn('test'); + $query = $qb->getQuery(); + $query = $query->getQuery(); + $this->assertEquals(array('address.subAddress.subAddress.subAddress.testFieldName' => array('$nin' => array('test'))), $query['query']); + } + + public function testNewObjectIsPrepared() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->update() + ->field('address.subAddress.subAddress.subAddress.test')->popFirst(); + $query = $qb->getQuery(); + $query = $query->getQuery(); + $this->assertEquals(array('$pop' => array('address.subAddress.subAddress.subAddress.testFieldName' => 1)), $query['newObj']); + } + + public function testReferencesUsesMinimalKeys() + { + $dm = $this->getMockBuilder('Doctrine\\ODM\\MongoDB\\DocumentManager') + ->disableOriginalConstructor() + ->getMock(); + $uw = $this->getMockBuilder('Doctrine\ODM\MongoDB\UnitOfWork') + ->disableOriginalConstructor() + ->getMock(); + $documentPersister = $this->getMockBuilder('Doctrine\ODM\MongoDB\Persisters\DocumentPersister') + ->disableOriginalConstructor() + ->getMock(); + $class = $this->getMockBuilder('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $expected = array('foo.$id' => '1234'); + + $dm->expects($this->once()) + ->method('createDBRef') + ->will($this->returnValue(array('$ref' => 'coll', '$id' => '1234', '$db' => 'db'))); + $dm->expects($this->once()) + ->method('getUnitOfWork') + ->will($this->returnValue($uw)); + $uw->expects($this->once()) + ->method('getDocumentPersister') + ->will($this->returnValue($documentPersister)); + $documentPersister->expects($this->once()) + ->method('prepareQuery') + ->with($expected) + ->will($this->returnValue($expected)); + $class->expects($this->once()) + ->method('getFieldMapping') + ->will($this->returnValue(array('targetDocument' => 'Foo'))); + + $expr = new Expr($dm, '$'); + $expr->setClassMetadata($class); + $expr->field('foo')->references(new \stdClass()); + + $this->assertEquals($expected, $expr->getQuery(), '->references() uses just $id if a targetDocument is set'); + } + + public function testReferencesUsesAllKeys() + { + $dm = $this->getMockBuilder('Doctrine\\ODM\\MongoDB\\DocumentManager') + ->disableOriginalConstructor() + ->getMock(); + $uw = $this->getMockBuilder('Doctrine\ODM\MongoDB\UnitOfWork') + ->disableOriginalConstructor() + ->getMock(); + $documentPersister = $this->getMockBuilder('Doctrine\ODM\MongoDB\Persisters\DocumentPersister') + ->disableOriginalConstructor() + ->getMock(); + $class = $this->getMockBuilder('Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $expected = array('foo.$ref' => 'coll', 'foo.$id' => '1234', 'foo.$db' => 'db'); + + $dm->expects($this->once()) + ->method('createDBRef') + ->will($this->returnValue(array('$ref' => 'coll', '$id' => '1234', '$db' => 'db'))); + $dm->expects($this->once()) + ->method('getUnitOfWork') + ->will($this->returnValue($uw)); + $uw->expects($this->once()) + ->method('getDocumentPersister') + ->will($this->returnValue($documentPersister)); + $documentPersister->expects($this->once()) + ->method('prepareQuery') + ->with($expected) + ->will($this->returnValue($expected)); + $class->expects($this->once()) + ->method('getFieldMapping') + ->will($this->returnValue(array())); + + $expr = new Expr($dm, '$'); + $expr->setClassMetadata($class); + $expr->field('foo')->references(new \stdClass()); + + $this->assertEquals($expected, $expr->getQuery(), '->references() uses all keys if no targetDocument is set'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FieldExtractorTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FieldExtractorTest.php new file mode 100644 index 00000000..f57629aa --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FieldExtractorTest.php @@ -0,0 +1,88 @@ +assertFieldsExtracted($query, $fields); + } + + public function getQueriesAndFields() + { + return array( + array( + array('fieldName' => 1), + array('fieldName') + ), + array( + array('fieldName' => array( + '$elemMatch' => array( + 'embedded' => 1 + ) + )), + array('fieldName.embedded') + ), + array( + array('fieldName' => array( + '$in' => array(1) + )), + array('fieldName') + ), + array( + array('fieldName' => array( + '$gt' => 1 + )), + array('fieldName') + ), + array( + array('$or' => array( + array( + 'fieldName1' => array( + '$in' => array(1) + ) + ), + array( + 'fieldName2' => array( + '$in' => array(1) + ) + ), + array( + 'fieldName3' => 1 + ) + )), + array('fieldName1', 'fieldName2', 'fieldName3') + ), + array( + array('$and' => array( + array( + 'fieldName1' => array( + '$in' => array(1) + ) + ), + array( + 'fieldName2' => array( + '$in' => array(1) + ) + ), + array( + 'fieldName3' => 1 + ) + )), + array('fieldName1', 'fieldName2', 'fieldName3') + ) + ); + } + + private function assertFieldsExtracted(array $query, array $fields) + { + $extractor = new FieldExtractor($query); + $this->assertEquals($fields, $extractor->getFields()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/BsonFilterTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/BsonFilterTest.php new file mode 100644 index 00000000..47f0bc55 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/BsonFilterTest.php @@ -0,0 +1,22 @@ +dm); + $filter->getParameter('doesnotexist'); + } + + public function testSetParameter() + { + $filter = new Filter($this->dm); + $filter->setParameter('username', 'Tim'); + $this->assertEquals('Tim', $filter->getParameter('username')); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/Filter.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/Filter.php new file mode 100644 index 00000000..2e23b9b7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/Filter/Filter.php @@ -0,0 +1,16 @@ +name == $this->parameters['class']) + ? array($this->parameters['field'] => $this->parameters['value']) + : array(); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FilterCollectionTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FilterCollectionTest.php new file mode 100644 index 00000000..c2ccf134 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Query/FilterCollectionTest.php @@ -0,0 +1,85 @@ +dm->getFilterCollection(); + + $this->assertCount(0, $filterCollection->getEnabledFilters()); + + $filterCollection->enable('testFilter'); + + $enabledFilters = $filterCollection->getEnabledFilters(); + $this->assertCount(1, $enabledFilters); + $this->assertContainsOnly('Doctrine\ODM\MongoDB\Query\Filter\BsonFilter', $enabledFilters); + + $filterCollection->disable('testFilter'); + $this->assertCount(0, $filterCollection->getEnabledFilters()); + } + + public function testHasFilter() + { + $filterCollection = $this->dm->getFilterCollection(); + + $this->assertTrue($filterCollection->has('testFilter')); + $this->assertFalse($filterCollection->has('fakeFilter')); + } + + /** + * @depends testEnable + */ + public function testIsEnabled() + { + $filterCollection = $this->dm->getFilterCollection(); + + $this->assertFalse($filterCollection->isEnabled('testFilter')); + + $filterCollection->enable('testFilter'); + + $this->assertTrue($filterCollection->isEnabled('testFilter')); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetFilterInvalidArgument() + { + $filterCollection = $this->dm->getFilterCollection(); + $filterCollection->getFilter('testFilter'); + } + + public function testGetFilter() + { + $filterCollection = $this->dm->getFilterCollection(); + $filterCollection->enable('testFilter'); + $this->assertInstanceOf('Doctrine\ODM\MongoDB\Tests\Query\Filter\Filter', $filterCollection->getFilter('testFilter')); + } + + public function testGetFilterCriteria() + { + $filterCollection = $this->dm->getFilterCollection(); + $metadata = $this->dm->getClassMetadata('Documents\User'); + $criteria = $filterCollection->getFilterCriteria($metadata); + + $this->assertTrue(is_array($criteria)); + $this->assertEquals(0, count($criteria)); + + $this->enableUserFilter(); + $criteria = $filterCollection->getFilterCriteria($metadata); + $this->assertTrue(is_array($criteria)); + $this->assertArrayHasKey('username', $criteria); + $this->assertEquals($criteria['username'], 'Tim'); + } + + protected function enableUserFilter() + { + $this->dm->getFilterCollection()->enable('testFilter'); + $testFilter = $this->dm->getFilterCollection()->getFilter('testFilter'); + $testFilter->setParameter('class', 'Documents\User'); + $testFilter->setParameter('field', 'username'); + $testFilter->setParameter('value', 'Tim'); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php new file mode 100644 index 00000000..2f46cdb9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php @@ -0,0 +1,218 @@ +dm->createQueryBuilder(__NAMESPACE__.'\Person') + ->exclude('comments') + ->select('comments') + ->selectSlice('comments', 0, 10); + $query = $qb->getQuery(); + $results = $query->execute(); + } + + public function testThatOrAcceptsAnotherQuery() + { + $kris = new Person('Kris'); + $chris = new Person('Chris'); + $this->dm->persist($kris); + $this->dm->persist($chris); + $this->dm->flush(); + + $class = __NAMESPACE__.'\Person'; + $expression1 = array('firstName' => 'Kris'); + $expression2 = array('firstName' => 'Chris'); + + $qb = $this->dm->createQueryBuilder($class); + $qb->addOr($qb->expr()->field('firstName')->equals('Kris')); + $qb->addOr($qb->expr()->field('firstName')->equals('Chris')); + + $this->assertEquals(array('$or' => array( + array('firstName' => 'Kris'), + array('firstName' => 'Chris') + )), $qb->getQueryArray()); + + $query = $qb->getQuery(); + $users = $query->execute(); + + $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $users); + $this->assertEquals(2, count($users)); + } + + public function testReferences() + { + $kris = new Person('Kris'); + $jon = new Person('Jon'); + + $this->dm->persist($kris); + $this->dm->persist($jon); + $this->dm->flush(); + + $kris->bestFriend = $jon; + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\Person'); + $qb->field('bestFriend')->references($jon); + + $queryArray = $qb->getQueryArray(); + $this->assertEquals(array( + 'bestFriend.$ref' => 'people', + 'bestFriend.$id' => new \MongoId($jon->id), + 'bestFriend.$db' => 'doctrine_odm_tests', + ), $queryArray); + + $query = $qb->getQuery(); + + $this->assertEquals(1, $query->count()); + $this->assertSame($kris, $query->getSingleResult()); + } + + public function testIncludesReferenceTo() + { + $kris = new Person('Kris'); + $jon = new Person('Jon'); + + $this->dm->persist($kris); + $this->dm->persist($jon); + $this->dm->flush(); + + $jon->friends[] = $kris; + $this->dm->flush(); + + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\Person'); + $qb->field('friends')->includesReferenceTo($kris); + + $queryArray = $qb->getQueryArray(); + $this->assertEquals(array( + 'friends' => array( + '$elemMatch' => array( + '$ref' => 'people', + '$id' => new \MongoId($kris->id), + '$db' => 'doctrine_odm_tests', + ), + ), + ), $queryArray); + + $query = $qb->getQuery(); + + $this->assertEquals(1, $query->count()); + $this->assertSame($jon, $query->getSingleResult()); + } + + public function testQueryIdIn() + { + $user = new \Documents\User(); + $user->setUsername('jwage'); + $this->dm->persist($user); + $this->dm->flush(); + + $mongoId = new \MongoId($user->getId()); + $ids = array($mongoId); + + $qb = $this->dm->createQueryBuilder('Documents\User') + ->field('_id')->in($ids); + $query = $qb->getQuery(); + $results = $query->toArray(); + $this->assertEquals(1, count($results)); + } + + public function testEmbeddedSet() + { + $qb = $this->dm->createQueryBuilder('Documents\User') + ->insert() + ->field('testInt')->set('0') + ->field('intfields.intone')->set('1') + ->field('intfields.inttwo')->set('2'); + $this->assertEquals(array('testInt' => 0, 'intfields' => array('intone' => 1, 'inttwo' => 2)), $qb->getNewObj()); + } + + public function testElemMatch() + { + $refId = '000000000000000000000001'; + + $qb = $this->dm->createQueryBuilder('Documents\User'); + $embeddedQb = $this->dm->createQueryBuilder('Documents\Phonenumber'); + + $qb->field('phonenumbers')->elemMatch($embeddedQb->expr() + ->field('lastCalledBy.id')->equals($refId) + ); + $query = $qb->getQuery(); + + $expectedQuery = array('phonenumbers' => array('$elemMatch' => array('lastCalledBy.$id' => new \MongoId($refId)))); + $this->assertEquals($expectedQuery, $query->debug()); + } + + public function testQueryWithMultipleEmbeddedDocuments() + { + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\EmbedTest') + ->find() + ->field('embeddedOne.embeddedOne.embeddedMany.embeddedOne.name')->equals('Foo'); + $query = $qb->getQuery(); + $this->assertEquals(array('eO.eO.e1.eO.n' => 'Foo'), $query->debug()); + } + + public function testQueryWithMultipleEmbeddedDocumentsAndReference() + { + $qb = $this->dm->createQueryBuilder(__NAMESPACE__.'\EmbedTest') + ->find() + ->field('embeddedOne.embeddedOne.embeddedMany.embeddedOne.pet.owner.id')->equals('Foo'); + $query = $qb->getQuery(); + $debug = $query->debug(); + + $this->assertTrue(array_key_exists('eO.eO.e1.eO.eP.pO._id', $debug)); + $this->assertInstanceOf('\MongoId', $debug['eO.eO.e1.eO.eP.pO._id']); + } +} + +/** @ODM\Document(collection="people") */ +class Person +{ + /** @ODM\Id */ + public $id; + + /** @ODM\String */ + public $firstName; + + /** @ODM\ReferenceOne */ + public $bestFriend; + + /** @ODM\ReferenceMany */ + public $friends = array(); + + public function __construct($firstName) + { + $this->firstName = $firstName; + } +} + +/** @ODM\EmbeddedDocument */ +class Pet +{ + /** @ODM\ReferenceOne(name="pO", targetDocument="Doctrine\ODM\MongoDB\Tests\Person") */ + public $owner; +} + +/** @ODM\EmbeddedDocument */ +class EmbedTest +{ + /** @ODM\EmbedOne(name="eO", targetDocument="Doctrine\ODM\MongoDB\Tests\EmbedTest") */ + public $embeddedOne; + + /** @ODM\EmbedMany(name="e1", targetDocument="Doctrine\ODM\MongoDB\Tests\EmbedTest") */ + public $embeddedMany; + + /** @ODM\String(name="n") */ + public $name; + + /** @ODM\ReferenceOne(name="p", targetDocument="Doctrine\ODM\MongoDB\Tests\Person") */ + public $person; + + /** @ODM\EmbedOne(name="eP", targetDocument="Doctrine\ODM\MongoDB\Tests\Pet") */ + public $pet; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php new file mode 100644 index 00000000..67d89584 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php @@ -0,0 +1,331 @@ + + */ +class SchemaManagerTest extends \Doctrine\ODM\MongoDB\Tests\BaseTest +{ + protected $uow; + protected $dm; + + public function setUp() + { + $this->uow = $this->getUnitOfWork(); + $documentPersister = $this->getDocumentPersister(); + $this->uow->expects($this->any()) + ->method('getDocumentPersister') + ->will($this->returnValue($documentPersister)); + + $this->dm = $this->getDocumentManager(); + $this->dm->setSchemaManager(new \Doctrine\ODM\MongoDB\SchemaManager($this->dm, $this->dm->getMetadataFactory())); + $this->dm->setUnitOfWork($this->uow); + } + + public function tearDown() + { + unset ($this->dm); + } + + /** + * @dataProvider getIndexedClasses + */ + public function testEnsureIndexes(array $classes) + { + foreach ($classes as $className) { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('ensureIndex'); + $this->dm->setDocumentCollection($className, $collection); + } + + $this->dm->getSchemaManager()->ensureIndexes(); + } + + public function getIndexedClasses() + { + return array( + array(array('Documents\SimpleReferenceUser', 'Documents\Comment', 'Documents\CmsArticle', 'Documents\CmsAddress', 'Documents\CmsComment', 'Documents\CmsProduct')) + ); + } + + public function testEnsureDocumentIndexes() + { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('ensureIndex'); + $this->dm->setDocumentCollection('Documents\CmsArticle', $collection); + $this->dm->getSchemaManager()->ensureDocumentIndexes('Documents\CmsArticle'); + } + + public function testEnsureDocumentIndexesWithTwoLevelInheritance() + { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('ensureIndex'); + $this->dm->setDocumentCollection('Documents\CmsProduct', $collection); + $this->dm->getSchemaManager()->ensureDocumentIndexes('Documents\CmsProduct'); + } + + public function testEnsureDocumentIndexesWithTimeout() + { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('ensureIndex') + ->with($this->anything(), $this->callback(function($o) { + return isset($o['timeout']) && $o['timeout'] === 10000; + })); + $this->dm->setDocumentCollection('Documents\CmsArticle', $collection); + $this->dm->getSchemaManager()->ensureDocumentIndexes('Documents\CmsArticle', 10000); + } + + /** + * @dataProvider getIndexedClasses + */ + public function testDeleteIndexes(array $classes) + { + $metadatas = array(); + foreach ($classes as $className) { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('deleteIndexes'); + $metadatas[] = (object) array('name' => $className, 'isMappedSuperclass' => false, 'isEmbeddedDocument' => false); + + $this->dm->setDocumentCollection($className, $collection); + } + + $metadataFactory = $this->getMetadataFactory(); + $metadataFactory->expects($this->once()) + ->method('getAllMetadata') + ->will($this->returnValue($metadatas)); + + $this->dm->setMetadataFactory($metadataFactory); + $this->dm->setSchemaManager(new \Doctrine\ODM\MongoDB\SchemaManager($this->dm, $metadataFactory)); + + $this->dm->getSchemaManager()->deleteIndexes(); + } + + public function testDeleteDocumentIndexes() + { + $collection = $this->getDocumentCollection(); + $collection->expects($this->once()) + ->method('deleteIndexes'); + $this->dm->setDocumentCollection('Documents\CmsArticle', $collection); + + $this->dm->getSchemaManager()->deleteDocumentIndexes('Documents\CmsArticle'); + } + + public function testCreateDocumentCollection() + { + $className = 'Documents\CmsArticle'; + $classMetadata = $this->getClassMetadata($className); + $classMetadata->expects($this->once()) + ->method('getCollection'); + $classMetadata->expects($this->once()) + ->method('getCollectionCapped'); + $classMetadata->expects($this->once()) + ->method('getCollectionSize'); + $classMetadata->expects($this->once()) + ->method('getCollectionMax'); + + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('createCollection'); + $this->dm->setDocumentDatabase($className, $documentDatabase); + + $this->dm->setClassMetadata($className, $classMetadata); + + $this->dm->getSchemaManager()->createDocumentCollection($className); + } + + /** + * @dataProvider getIndexedClasses + */ + public function testCreateCollections(array $classes) + { + $metadatas = array(); + foreach ($classes as $className) { + $metadatas[] = (object) array('name' => $className, 'isMappedSuperclass' => false, 'isEmbeddedDocument' => false); + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('createCollection'); + $this->dm->setDocumentDatabase($className, $documentDatabase); + } + + $metadataFactory = $this->getMetadataFactory(); + $metadataFactory->expects($this->once()) + ->method('getAllMetadata') + ->will($this->returnValue($metadatas)); + + $this->dm->setMetadataFactory($metadataFactory); + $this->dm->setSchemaManager(new \Doctrine\ODM\MongoDB\SchemaManager($this->dm, $metadataFactory)); + + $this->dm->getSchemaManager()->createCollections(); + } + + /** + * @dataProvider getIndexedClasses + */ + public function testDropCollections(array $classes) + { + $metadatas = array(); + foreach ($classes as $className) { + $metadata = $this->getClassMetadata($className); + $metadata->expects($this->once()) + ->method('getCollection') + ->will($this->returnValue($className)); + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('dropCollection') + ->with($className); + $this->dm->setDocumentDatabase($className, $documentDatabase); + $this->dm->setClassMetadata($className, $metadata); + $metadatas[] = $metadata; + } + + $metadataFactory = $this->getMetadataFactory(); + $metadataFactory->expects($this->once()) + ->method('getAllMetadata') + ->will($this->returnValue($metadatas)); + + $this->dm->setMetadataFactory($metadataFactory); + $this->dm->setSchemaManager(new \Doctrine\ODM\MongoDB\SchemaManager($this->dm, $metadataFactory)); + + $this->dm->getSchemaManager()->dropCollections(); + } + + public function testDropDocumentCollection() + { + $className = 'Documents\CmsArticle'; + $collectionName = 'cms_articles'; + $classMetadata = $this->getClassMetadata($className); + $classMetadata->expects($this->once()) + ->method('getCollection') + ->will($this->returnValue($collectionName)); + + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('dropCollection') + ->with($collectionName); + $this->dm->setDocumentDatabase($className, $documentDatabase); + + $this->dm->setClassMetadata($className, $classMetadata); + + $this->dm->getSchemaManager()->dropDocumentCollection($className); + } + + public function testCreateDocumentDatabase() + { + $className = 'Documents\CmsArticle'; + $dbName = 'test_db'; + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('execute'); + $this->dm->setDocumentDatabase($className, $documentDatabase); + + $this->dm->getSchemaManager()->createDocumentDatabase($className); + } + + public function testDropDocumentDatabase() + { + $className = 'Documents\CmsArticle'; + $dbName = 'test_db'; + + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('drop'); + $this->dm->setDocumentDatabase($className, $documentDatabase); + + $this->dm->getSchemaManager()->dropDocumentDatabase($className); + } + + /** + * @dataProvider getIndexedClasses + */ + public function testDropDatabases(array $classes) + { + $metadatas = array(); + foreach ($classes as $className) { + $documentDatabase = $this->getDocumentDatabase($className); + $documentDatabase->expects($this->once()) + ->method('drop'); + $this->dm->setDocumentDatabase($className, $documentDatabase); + $metadatas[] = (object) array('name' => $className, 'isMappedSuperclass' => false, 'isEmbeddedDocument' => false); + } + + $metadataFactory = $this->getMetadataFactory(); + $metadataFactory->expects($this->once()) + ->method('getAllMetadata') + ->will($this->returnValue($metadatas)); + + $this->dm->setMetadataFactory($metadataFactory); + $this->dm->setSchemaManager(new \Doctrine\ODM\MongoDB\SchemaManager($this->dm, $metadataFactory)); + + $this->dm->getSchemaManager()->dropDatabases(); + } + + protected function getDocumentManager() + { + $config = new Configuration(); + + $config->setProxyDir(__DIR__ . '/../../../../Proxies'); + $config->setProxyNamespace('Proxies'); + + $config->setHydratorDir(__DIR__ . '/../../../../Hydrators'); + $config->setHydratorNamespace('Hydrators'); + + $config->setDefaultDB('doctrine_odm_tests'); + + $reader = new AnnotationReader(); + $config->setMetadataDriverImpl(new AnnotationDriver($reader, __DIR__ . '/../../../../Documents')); + return DocumentManagerMock::create($this->getConnection(), $config); + } + + protected function getDocumentPersister() + { + return $this->getMockBuilder('Doctrine\ODM\MongoDB\Persisters\DocumentPersister') + ->disableOriginalConstructor() + ->getMock(); + } + + protected function getUnitOfWork() + { + return $this->getMockBuilder('Doctrine\ODM\MongoDB\UnitOfWork') + ->disableOriginalConstructor() + ->getMock(); + } + + protected function getConnection() + { + return $this->getMock('Doctrine\MongoDB\Connection', array('selectDatabase'), array(), '', false, false); + } + + protected function getMetadataFactory() + { + return $this->getMock('Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory', array('getAllMetadata', 'getMetadataFor'), array(), '', false, false); + } + + protected function getClassMetadata($className) + { + $classMetadata = $this->getMock('Doctrine\ODM\MongoDB\Mapping\ClassMetadata', array('getCollection', 'getCollectionCapped', 'getCollectionSize', 'getCollectionMax'), array($className), '', true, false); + $classMetadata->name = $className; + return $classMetadata; + } + + protected function getDocumentCollection() + { + return $this->getMock('Doctrine\MongoDB\Collection', array('ensureIndex', 'deleteIndex', 'deleteIndexes', 'selectCollection'), array(), '', false, false); + } + + protected function getDocumentDatabase($className) + { + $documentDatabase = $this->getMock('Doctrine\MongoDB\Database', array('authenticate', 'command', 'createCollection', 'createDBRef', 'drop', 'dropCollection', 'execute', 'forceError', 'getDatabaseRef', '__get', 'getGridFS', 'getProfilingLevel', 'getLastError'), array(), '', false, false); + return $documentDatabase; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Tools/DocumentGeneratorTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Tools/DocumentGeneratorTest.php new file mode 100644 index 00000000..bf038a3b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/Tools/DocumentGeneratorTest.php @@ -0,0 +1,189 @@ +namespace = uniqid("doctrine_"); + $this->tmpDir = \sys_get_temp_dir(); + \mkdir($this->tmpDir . \DIRECTORY_SEPARATOR . $this->namespace); + $this->generator = new DocumentGenerator(); + $this->generator->setGenerateAnnotations(true); + $this->generator->setGenerateStubMethods(true); + $this->generator->setRegenerateDocumentIfExists(false); + $this->generator->setUpdateDocumentIfExists(true); + } + + public function tearDown() + { + $ri = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->tmpDir . '/' . $this->namespace)); + foreach ($ri AS $file) { + /* @var $file \SplFileInfo */ + if ($file->isFile()) { + \unlink($file->getPathname()); + } + } + rmdir($this->tmpDir . '/' . $this->namespace); + } + + public function generateBookDocumentFixture() + { + $metadata = new ClassMetadataInfo($this->namespace . '\DocumentGeneratorBook'); + $metadata->namespace = $this->namespace; + $metadata->customRepositoryClassName = $this->namespace . '\DocumentGeneratorBookRepository'; + + $metadata->collection = 'book'; + $metadata->mapField(array('fieldName' => 'name', 'type' => 'string')); + $metadata->mapField(array('fieldName' => 'status', 'type' => 'string')); + $metadata->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); + $metadata->mapOneReference(array('fieldName' => 'author', 'targetDocument' => 'Doctrine\ODM\MongoDB\Tests\Tools\DocumentGeneratorAuthor')); + $metadata->mapManyReference(array( + 'fieldName' => 'comments', + 'targetDocument' => 'Doctrine\ODM\MongoDB\Tests\Tools\DocumentGeneratorComment' + )); + $metadata->addLifecycleCallback('loading', 'postLoad'); + $metadata->addLifecycleCallback('willBeRemoved', 'preRemove'); + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + + $this->generator->writeDocumentClass($metadata, $this->tmpDir); + + return $metadata; + } + + /** + * @param ClassMetadataInfo $metadata + * @return DocumentGeneratorBook + */ + public function newInstance($metadata) + { + $path = $this->tmpDir . '/'. $this->namespace . '/DocumentGeneratorBook.php'; + $this->assertFileExists($path); + require_once $path; + + return new $metadata->name; + } + + public function testGeneratedDocumentClass() + { + $metadata = $this->generateBookDocumentFixture(); + + $book = $this->newInstance($metadata); + + $this->assertTrue(class_exists($metadata->name), "Class does not exist."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', '__construct'), "DocumentGeneratorBook::__construct() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'getId'), "DocumentGeneratorBook::getId() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'setName'), "DocumentGeneratorBook::setName() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'getName'), "DocumentGeneratorBook::getName() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'setAuthor'), "DocumentGeneratorBook::setAuthor() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'getAuthor'), "DocumentGeneratorBook::getAuthor() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'getComments'), "DocumentGeneratorBook::getComments() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'addComment'), "DocumentGeneratorBook::addComment() missing."); + $this->assertTrue(method_exists($metadata->namespace . '\DocumentGeneratorBook', 'removeComment'), "EntityGeneratorBook::removeComment() missing."); + + $book->setName('Jonathan H. Wage'); + $this->assertEquals('Jonathan H. Wage', $book->getName()); + + $author = new DocumentGeneratorAuthor(); + $book->setAuthor($author); + $this->assertEquals($author, $book->getAuthor()); + + $comment = new DocumentGeneratorComment(); + $book->addComment($comment); + $this->assertInstanceOf('Doctrine\Common\Collections\ArrayCollection', $book->getComments()); + $this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array($comment)), $book->getComments()); + $book->removeComment($comment); + $this->assertEquals(new \Doctrine\Common\Collections\ArrayCollection(array()), $book->getComments()); + } + + public function testDocumentUpdatingWorks() + { + $metadata = $this->generateBookDocumentFixture(); + $metadata->mapField(array('fieldName' => 'test', 'type' => 'string')); + + $this->generator->writeDocumentClass($metadata, $this->tmpDir); + + $this->assertFileExists($this->tmpDir . "/" . $this->namespace . "/DocumentGeneratorBook.php"); + + $book = $this->newInstance($metadata); + $reflClass = new \ReflectionClass($metadata->name); + + $this->assertTrue($reflClass->hasProperty('name'), "Regenerating keeps property 'name'."); + $this->assertTrue($reflClass->hasProperty('status'), "Regenerating keeps property 'status'."); + $this->assertTrue($reflClass->hasProperty('id'), "Regenerating keeps property 'id'."); + + $this->assertTrue($reflClass->hasProperty('test'), "Check for property test failed."); + $this->assertTrue($reflClass->getProperty('test')->isProtected(), "Check for protected property test failed."); + $this->assertTrue($reflClass->hasMethod('getTest'), "Check for method 'getTest' failed."); + $this->assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed."); + $this->assertTrue($reflClass->hasMethod('setTest'), "Check for method 'getTest' failed."); + $this->assertTrue($reflClass->getMethod('getTest')->isPublic(), "Check for public visibility of method 'getTest' failed."); + } + + public function testDocumentExtendsStdClass() + { + $this->generator->setClassToExtend('stdClass'); + $metadata = $this->generateBookDocumentFixture(); + + $book = $this->newInstance($metadata); + $this->assertInstanceOf('stdClass', $book); + } + + public function testLifecycleCallbacks() + { + $metadata = $this->generateBookDocumentFixture(); + + $book = $this->newInstance($metadata); + $reflClass = new \ReflectionClass($metadata->name); + + $this->assertTrue($reflClass->hasMethod('loading'), "Check for postLoad lifecycle callback."); + $this->assertTrue($reflClass->hasMethod('willBeRemoved'), "Check for preRemove lifecycle callback."); + } + + public function testLoadMetadata() + { + $metadata = $this->generateBookDocumentFixture(); + + $book = $this->newInstance($metadata); + + $cm = new \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo($metadata->name); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $driver = new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader); + $driver->loadMetadataForClass($cm->name, $cm); + + $this->assertEquals($cm->getCollection(), $metadata->getCollection()); + $this->assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks); + $this->assertEquals($cm->identifier, $metadata->identifier); + $this->assertEquals($cm->idGenerator, $metadata->idGenerator); + $this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName); + } + + public function testLoadPrefixedMetadata() + { + $metadata = $this->generateBookDocumentFixture(); + + $book = $this->newInstance($metadata); + + $cm = new \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo($metadata->name); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $driver = new \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver($reader); + $driver->loadMetadataForClass($cm->name, $cm); + + $this->assertEquals($cm->getCollection(), $metadata->getCollection()); + $this->assertEquals($cm->lifecycleCallbacks, $metadata->lifecycleCallbacks); + $this->assertEquals($cm->identifier, $metadata->identifier); + $this->assertEquals($cm->idGenerator, $metadata->idGenerator); + $this->assertEquals($cm->customRepositoryClassName, $metadata->customRepositoryClassName); + } +} + +class DocumentGeneratorAuthor {} +class DocumentGeneratorComment {} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php new file mode 100644 index 00000000..bc433dd1 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -0,0 +1,513 @@ +dm = DocumentManagerMock::create(new ConnectionMock()); + $this->uow = $this->dm->getUnitOfWork(); + } + + protected function tearDown() + { + unset($this->dm, $this->uow); + } + + public function testScheduleForInsert() + { + $class = $this->dm->getClassMetadata('Documents\ForumUser'); + $user = new ForumUser(); + $this->assertFalse($this->uow->isScheduledForInsert($user)); + $this->uow->scheduleForInsert($class, $user); + $this->assertTrue($this->uow->isScheduledForInsert($user)); + } + + public function testScheduleForInsertUpsert() + { + $class = $this->dm->getClassMetadata('Documents\ForumUser'); + $user = new ForumUser(); + $user->id = 1; + $this->assertFalse($this->uow->isScheduledForInsert($user)); + $this->assertFalse($this->uow->isScheduledForUpsert($user)); + $this->uow->scheduleForInsert($class, $user); + $this->assertTrue($this->uow->isScheduledForInsert($user)); + $this->assertTrue($this->uow->isScheduledForUpsert($user)); + } + + public function testRegisterRemovedOnNewEntityIsIgnored() + { + $user = new ForumUser(); + $user->username = 'romanb'; + $this->assertFalse($this->uow->isScheduledForDelete($user)); + $this->uow->scheduleForDelete($user); + $this->assertFalse($this->uow->isScheduledForDelete($user)); + } + + + /* Operational tests */ + + public function testSavingSingleEntityWithIdentityColumnForcesInsert() + { + // Setup fake persister and id generator for identity generation + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata('Documents\ForumUser'); + $userPersister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Documents\ForumUser', $userPersister); + + // Test + $user = new ForumUser(); + $user->username = 'romanb'; + $this->uow->persist($user); + + // Check + $this->assertEquals(0, count($userPersister->getInserts())); + $this->assertEquals(0, count($userPersister->getUpdates())); + $this->assertEquals(0, count($userPersister->getDeletes())); + $this->assertFalse($this->uow->isInIdentityMap($user)); + // should no longer be scheduled for insert + $this->assertTrue($this->uow->isScheduledForInsert($user)); + + // Now lets check whether a subsequent commit() does anything + $userPersister->reset(); + + // Test + $this->uow->commit(); + + // Check. + $this->assertEquals(1, count($userPersister->getInserts())); + $this->assertEquals(0, count($userPersister->getUpdates())); + $this->assertEquals(0, count($userPersister->getDeletes())); + + // should have an id + $this->assertTrue(is_numeric($user->id)); + } + + /** + * Tests a scenario where a save() operation is cascaded from a ForumUser + * to its associated ForumAvatar, both entities using IDENTITY id generation. + */ + public function testCascadedIdentityColumnInsert() + { + // Setup fake persister and id generator for identity generation + //ForumUser + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata('Documents\ForumUser'); + $userPersister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Documents\ForumUser', $userPersister); + + // ForumAvatar + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata('Documents\ForumAvatar'); + $avatarPersister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Documents\ForumAvatar', $avatarPersister); + + // Test + $user = new ForumUser(); + $user->username = 'romanb'; + $avatar = new ForumAvatar(); + $user->avatar = $avatar; + $this->uow->persist($user); // save cascaded to avatar + + $this->uow->commit(); + + $this->assertTrue(is_numeric($user->id)); + $this->assertTrue(is_numeric($avatar->id)); + + $this->assertEquals(1, count($userPersister->getInserts())); + $this->assertEquals(0, count($userPersister->getUpdates())); + $this->assertEquals(0, count($userPersister->getDeletes())); + + $this->assertEquals(1, count($avatarPersister->getInserts())); + $this->assertEquals(0, count($avatarPersister->getUpdates())); + $this->assertEquals(0, count($avatarPersister->getDeletes())); + } + + public function testChangeTrackingNotify() + { + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata("Doctrine\ODM\MongoDB\Tests\NotifyChangedDocument"); + $persister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Doctrine\ODM\MongoDB\Tests\NotifyChangedDocument', $persister); + + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata("Doctrine\ODM\MongoDB\Tests\NotifyChangedRelatedItem"); + $itemPersister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Doctrine\ODM\MongoDB\Tests\NotifyChangedRelatedItem', $itemPersister); + + $entity = new NotifyChangedDocument; + $entity->setData('thedata'); + $this->uow->persist($entity); + + $this->uow->commit(); + $this->assertEquals(1, count($persister->getInserts())); + $persister->reset(); + + $this->assertTrue($this->uow->isInIdentityMap($entity)); + + $entity->setData('newdata'); + $entity->setTransient('newtransientvalue'); + + $this->assertTrue($this->uow->isScheduledForDirtyCheck($entity)); + + $this->assertEquals(array('data' => array('thedata', 'newdata')), $this->uow->getDocumentChangeSet($entity)); + + $item = new NotifyChangedRelatedItem(); + $entity->getItems()->add($item); + $item->setOwner($entity); + $this->uow->persist($item); + + $this->uow->commit(); + $this->assertEquals(1, count($itemPersister->getInserts())); + $persister->reset(); + $itemPersister->reset(); + + $entity->getItems()->removeElement($item); + $item->setOwner(null); + $this->assertTrue($entity->getItems()->isDirty()); + $this->uow->commit(); + $updates = $itemPersister->getUpdates(); + $this->assertEquals(1, count($updates)); + $this->assertTrue($updates[0] === $item); + } + + public function testGetDocumentStateWithAssignedIdentity() + { + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata("Documents\CmsPhonenumber"); + $persister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Documents\CmsPhonenumber', $persister); + + $ph = new \Documents\CmsPhonenumber(); + $ph->phonenumber = '12345'; + + $this->assertEquals(UnitOfWork::STATE_NEW, $this->uow->getDocumentState($ph)); + $this->assertTrue($persister->isExistsCalled()); + + $persister->reset(); + + // if the document is already managed the exists() check should be skipped + $this->uow->registerManaged($ph, '12345', array()); + $this->assertEquals(UnitOfWork::STATE_MANAGED, $this->uow->getDocumentState($ph)); + $this->assertFalse($persister->isExistsCalled()); + $ph2 = new \Documents\CmsPhonenumber(); + $ph2->phonenumber = '12345'; + $this->assertEquals(UnitOfWork::STATE_DETACHED, $this->uow->getDocumentState($ph2)); + $this->assertFalse($persister->isExistsCalled()); + } + + /** + * @expectedException Doctrine\ODM\MongoDB\MongoDBException + */ + public function testThrowsOnPersistOfMappedSuperclass() + { + $documentManager = $this->getDocumentManager(); + $documentManager->setClassMetadata('Documents\Address', $this->getClassMetadata('Documents\Address', 'MappedSuperclass')); + $unitOfWork = $this->getUnitOfWork($documentManager); + $unitOfWork->persist(new \Documents\Address()); + } + + public function testParentAssociations() + { + $a = new ParentAssociationTest('a'); + $b = new ParentAssociationTest('b'); + $c = new ParentAssociationTest('c'); + $d = new ParentAssociationTest('c'); + + $documentManager = $this->getDocumentManager(); + $unitOfWork = $this->getUnitOfWork($documentManager); + $unitOfWork->setParentAssociation($b, array('name' => 'b'), $a, 'b'); + $unitOfWork->setParentAssociation($c, array('name' => 'c'), $b, 'b.c'); + $unitOfWork->setParentAssociation($d, array('name' => 'd'), $c, 'b.c.d'); + + $this->assertEquals(array(array('name' => 'd'), $c, 'b.c.d'), $unitOfWork->getParentAssociation($d)); + } + + public function testPreUpdateTriggeredWithEmptyChangeset() + { + $dm = DocumentManagerMock::create(); + $evm = $dm->getEventManager()->addEventSubscriber( + new \Doctrine\ODM\MongoDB\Tests\Mocks\PreUpdateListenerMock() + ); + $user = new \Documents\ForumUser(); + $user->username = '12345'; + + $dm->persist($user); + $dm->flush(); + + $user->username = '1234'; + $dm->persist($user); + $dm->flush(); + } + + public function testNotSaved() + { + $test = new \Documents\Functional\NotSaved(); + $test->name = 'test'; + $test->notSaved = 'Jon'; + $this->dm->persist($test); + + $this->uow->computeChangeSets(); + $changeset = $this->uow->getDocumentChangeSet($test); + $this->assertFalse(isset($changeset['notSaved'])); + } + + /** + * @dataProvider getScheduleForUpdateWithArraysTests + */ + public function testScheduleForUpdateWithArrays($origData, $updateData, $shouldInUpdate) + { + $pb = $this->getMockPersistenceBuilder(); + $class = $this->dm->getClassMetadata("Doctrine\ODM\MongoDB\Tests\ArrayTest"); + $persister = $this->getMockDocumentPersister($pb, $class); + $this->uow->setDocumentPersister('Doctrine\ODM\MongoDB\Tests\ArrayTest', $persister); + + $arrayTest = new ArrayTest($origData); + $this->uow->persist($arrayTest); + $this->uow->computeChangeSets(); + + $arrayTest->data = $updateData; + $this->uow->persist($arrayTest); + $this->uow->computeChangeSets(); + + $this->assertEquals($shouldInUpdate, $this->uow->isScheduledForUpdate($arrayTest)); + } + + public function getScheduleForUpdateWithArraysTests() + { + return array( + array( + null, + array('bar' => 'foo'), + true + ), + array( + array('foo' => 'bar'), + null, + true + ), + array( + array('foo' => 'bar'), + array('bar' => 'foo'), + true + ), + array( + array('foo' => 'bar'), + array('foo' => 'foo'), + true + ), + array( + array('foo' => 'bar'), + array('foo' => 'bar'), + false + ), + array( + array('foo' => 'bar'), + array('foo' => true), + true + ), + array( + array('foo' => 'bar'), + array('foo' => 99), + true + ), + array( + array('foo' => 99), + array('foo' => true), + true + ), + array( + array('foo' => true), + array('foo' => true), + false + ), + ); + } + + protected function getDocumentManager() + { + return new \Stubs\DocumentManager(); + } + + protected function getUnitOfWork(DocumentManager $dm) + { + return new UnitOfWork($dm, $this->getMockEventManager(), $this->getMockHydratorFactory(), '$'); + } + + /** + * Gets mock HydratorFactory instance + * + * @return Doctrine\ODM\MongoDB\Hydrator\HydratorFactory + */ + private function getMockHydratorFactory() + { + return $this->getMockBuilder('Doctrine\ODM\MongoDB\Hydrator\HydratorFactory') + ->disableOriginalClone() + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Gets mock EventManager instance + * + * @return Doctrine\Common\EventManager + */ + private function getMockEventManager() + { + return $this->getMockBuilder('Doctrine\Common\EventManager') + ->disableOriginalClone() + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockPersistenceBuilder() + { + return $this->getMock('Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder', array(), array(), '', false, false); + } + + private function getMockDocumentPersister(PersistenceBuilder $pb, ClassMetadata $class) + { + return new DocumentPersisterMock($pb, $this->dm, $this->dm->getEventManager(), $this->uow, $this->dm->getHydratorFactory(), $class, '$'); + } + + protected function getClassMetadata($class, $flag) + { + $classMetadata = new ClassMetadata($class); + $classMetadata->{'is' . ucfirst($flag)} = true; + return $classMetadata; + } +} + +class ParentAssociationTest +{ + public $name; + public function __construct($name) + { + $this->name = $name; + } +} + +/** + * @ODM\Document + */ +class NotifyChangedDocument implements \Doctrine\Common\NotifyPropertyChanged +{ + private $_listeners = array(); + /** + * @ODM\Id + */ + private $id; + /** + * @ODM\String + */ + private $data; + + private $transient; // not persisted + + /** @ODM\ReferenceMany(targetDocument="NotifyChangedRelatedItem") */ + private $items; + + public function __construct() { + $this->items = new \Doctrine\Common\Collections\ArrayCollection; + } + + public function getId() { + return $this->id; + } + + public function getItems() { + return $this->items; + } + + public function setTransient($value) { + if ($value != $this->transient) { + $this->_onPropertyChanged('transient', $this->transient, $value); + $this->transient = $value; + } + } + + public function getData() { + return $this->data; + } + + public function setData($data) { + if ($data != $this->data) { + $this->_onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + + public function addPropertyChangedListener(\Doctrine\Common\PropertyChangedListener $listener) + { + $this->_listeners[] = $listener; + } + + protected function _onPropertyChanged($propName, $oldValue, $newValue) { + if ($this->_listeners) { + foreach ($this->_listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } +} + +/** @ODM\Document */ +class NotifyChangedRelatedItem +{ + /** + * @ODM\Id + */ + private $id; + + /** @ODM\ReferenceOne(targetDocument="NotifyChangedDocument") */ + private $owner; + + public function getId() { + return $this->id; + } + + public function getOwner() { + return $this->owner; + } + + public function setOwner($owner) { + $this->owner = $owner; + } +} + +/** + * @ODM\Document + */ +class ArrayTest +{ + /** + * @ODM\Id + */ + private $id; + /** + * @ODM\Hash + */ + public $data; + + public function __construct($data) + { + $this->data = $data; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Account.php b/vendor/doctrine/mongodb-odm/tests/Documents/Account.php new file mode 100644 index 00000000..6ef6fb6a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Account.php @@ -0,0 +1,44 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setUser($user) + { + $this->user = $user; + } + + public function getUser() + { + return $this->user; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Address.php b/vendor/doctrine/mongodb-odm/tests/Documents/Address.php new file mode 100644 index 00000000..3b5a2847 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Address.php @@ -0,0 +1,80 @@ +subAddress = $subAddress; + } + + public function getSubAddress() + { + return $this->subAddress; + } + + public function getAddress() + { + return $this->address; + } + + public function setAddress($address) + { + $this->address = $address; + } + + public function getCity() + { + return $this->city; + } + + public function setCity($city) + { + $this->city = $city; + } + + public function getState() + { + return $this->state; + } + + public function setState($state) + { + $this->state = $state; + } + + public function getZipcode() + { + return $this->zipcode; + } + + public function setZipcode($zipcode) + { + $this->zipcode = $zipcode; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Agent.php b/vendor/doctrine/mongodb-odm/tests/Documents/Agent.php new file mode 100644 index 00000000..478ca6a4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Agent.php @@ -0,0 +1,20 @@ +name = $name; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function addSong(Song $song) + { + $this->songs[] = $song; + } + + public function getSongs() + { + return $this->songs; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Article.php b/vendor/doctrine/mongodb-odm/tests/Documents/Article.php new file mode 100644 index 00000000..c10af016 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Article.php @@ -0,0 +1,81 @@ +id; + } + + public function getTitle() + { + return $this->title; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getBody() + { + return $this->body; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getCreatedAt() + { + return $this->createdAt; + } + + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + } + + public function addTag($tag) + { + $this->tags[] = $tag; + } + + public function removeTag($tag) + { + if ( ! in_array($tag, $this->tags)) + { + return; + } + unset($this->tags[array_search($tag, $this->tags)]); + } + + public function getTags() + { + return $this->tags; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Bar.php b/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Bar.php new file mode 100644 index 00000000..8910efde --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Bar.php @@ -0,0 +1,48 @@ +name = $name; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function addLocation(Location $location) + { + $this->locations[] = $location; + } + + public function getLocations() + { + return $this->locations; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Location.php b/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Location.php new file mode 100644 index 00000000..b0895f55 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Bars/Location.php @@ -0,0 +1,32 @@ +name = $name; + } + + public function getId() + { + return $this->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/mongodb-odm/tests/Documents/BaseCategory.php b/vendor/doctrine/mongodb-odm/tests/Documents/BaseCategory.php new file mode 100644 index 00000000..097f7fd0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/BaseCategory.php @@ -0,0 +1,45 @@ +name = $name; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function addChild(BaseCategory $child) + { + $this->children[] = $child; + } + + public function getChildren() + { + return $this->children; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/BaseDocument.php b/vendor/doctrine/mongodb-odm/tests/Documents/BaseDocument.php new file mode 100644 index 00000000..98bb9688 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/BaseDocument.php @@ -0,0 +1,30 @@ +inheritedProperty = $value; + } + + public function getInheritedProperty() + { + return $this->inheritedProperty; + } + + /** @ODM\PrePersist */ + public function prePersist() + { + $this->persisted = true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/BaseEmployee.php b/vendor/doctrine/mongodb-odm/tests/Documents/BaseEmployee.php new file mode 100644 index 00000000..5806d68a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/BaseEmployee.php @@ -0,0 +1,121 @@ +id; + } + + public function setId($val) + { + $this->id = $val; + return $this; + } + + public function getChanges() + { + return $this->changes; + } + + public function incrementChanges($num) + { + $this->changes = $this->changes + $num; + return $this; + } + + public function getNotes() + { + return $this->notes; + } + + public function addNote($note) + { + $this->notes[] = $note; + return $this; + } + + public function getName() + { + return $this->name; + } + + public function setName($val) + { + $this->name = $val; + return $this; + } + + public function getSalary() + { + return $this->salary; + } + + public function setSalary($val) + { + $this->salary = $val; + return $this; + } + + public function getStarted() + { + return $this->started; + } + + public function setStarted($val) + { + $this->started = $val; + return $this; + } + + public function getLeft() + { + return $this->left; + } + + public function setLeft($val) + { + $this->left = $val; + return $this; + } + + public function getAddress() + { + return $this->address; + } + + public function setAddress($val) + { + $this->address = $val; + return $this; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/BlogPost.php b/vendor/doctrine/mongodb-odm/tests/Documents/BlogPost.php new file mode 100644 index 00000000..0272cad0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/BlogPost.php @@ -0,0 +1,70 @@ +name = $name; + } + + public function getName() + { + return $this->name; + } + + public function addTag(Tag $tag) + { + $tag->addBlogPost($this); // synchronously updating inverse side + $this->tags[] = $tag; + } + + public function addComment(Comment $comment) + { + $comment->parent = $this; + $this->comments[] = $comment; + } + + public function setUser(User $user = null) + { + $this->user = $user; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/BrowseNode.php b/vendor/doctrine/mongodb-odm/tests/Documents/BrowseNode.php new file mode 100644 index 00000000..3cb8030f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/BrowseNode.php @@ -0,0 +1,37 @@ +name = $name; + $this->children = new \Doctrine\Common\Collections\ArrayCollection(); + } + + public function addChild(BrowseNode $child) + { + $child->parent = $this; + $this->children[] = $child; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Cart.php b/vendor/doctrine/mongodb-odm/tests/Documents/Cart.php new file mode 100644 index 00000000..69c1d145 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Cart.php @@ -0,0 +1,20 @@ +id; + } + + public function getUser() { + return $this->user; + } + + public function getCountry() { + return $this->country; + } + + public function getZipCode() { + return $this->zip; + } + + public function getCity() { + return $this->city; + } + + public function setUser(CmsUser $user) { + if ($this->user !== $user) { + $this->user = $user; + $user->setAddress($this); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CmsArticle.php b/vendor/doctrine/mongodb-odm/tests/Documents/CmsArticle.php new file mode 100644 index 00000000..42e3340c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CmsArticle.php @@ -0,0 +1,44 @@ +user = $author; + } + + public function addComment(CmsComment $comment) { + $this->comments[] = $comment; + $comment->setArticle($this); + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CmsComment.php b/vendor/doctrine/mongodb-odm/tests/Documents/CmsComment.php new file mode 100644 index 00000000..ba6934aa --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CmsComment.php @@ -0,0 +1,39 @@ +article = $article; + } + + public function __toString() { + return __CLASS__."[id=".$this->id."]"; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CmsContent.php b/vendor/doctrine/mongodb-odm/tests/Documents/CmsContent.php new file mode 100644 index 00000000..95663005 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CmsContent.php @@ -0,0 +1,16 @@ +name = $name; + } + + public function getName() { + return $this->name; + } + + public function addUser(CmsUser $user) { + $this->users[] = $user; + } + + public function getUsers() { + return $this->users; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CmsPage.php b/vendor/doctrine/mongodb-odm/tests/Documents/CmsPage.php new file mode 100644 index 00000000..1eede71b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CmsPage.php @@ -0,0 +1,24 @@ +user = $user; + } + + public function getUser() { + return $this->user; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CmsProduct.php b/vendor/doctrine/mongodb-odm/tests/Documents/CmsProduct.php new file mode 100644 index 00000000..01d1a4e0 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CmsProduct.php @@ -0,0 +1,16 @@ +phonenumbers = new ArrayCollection; + $this->articles = new ArrayCollection; + $this->groups = new ArrayCollection; + } + + public function getId() { + return $this->id; + } + + public function getStatus() { + return $this->status; + } + + public function getUsername() { + return $this->username; + } + + public function getName() { + return $this->name; + } + + /** + * Adds a phonenumber to the user. + * + * @param CmsPhonenumber $phone + */ + public function addPhonenumber(CmsPhonenumber $phone) { + $this->phonenumbers[] = $phone; + $phone->setUser($this); + } + + public function getPhonenumbers() { + return $this->phonenumbers; + } + + public function addArticle(CmsArticle $article) { + $this->articles[] = $article; + $article->setAuthor($this); + } + + public function addGroup(CmsGroup $group) { + $this->groups[] = $group; + $group->addUser($this); + } + + public function getGroups() { + return $this->groups; + } + + public function removePhonenumber($index) { + if (isset($this->phonenumbers[$index])) { + $ph = $this->phonenumbers[$index]; + unset($this->phonenumbers[$index]); + $ph->user = null; + return true; + } + return false; + } + + public function getAddress() { return $this->address; } + + public function setAddress(CmsAddress $address) { + if ($this->address !== $address) { + $this->address = $address; + $address->setUser($this); + } + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Comment.php b/vendor/doctrine/mongodb-odm/tests/Documents/Comment.php new file mode 100644 index 00000000..a48093c2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Comment.php @@ -0,0 +1,38 @@ +text = $text; + $this->date = $date; + $this->isByAdmin = $isByAdmin; + } + + public function getText() + { + return $this->text; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CommentRepository.php b/vendor/doctrine/mongodb-odm/tests/Documents/CommentRepository.php new file mode 100644 index 00000000..5e728395 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CommentRepository.php @@ -0,0 +1,22 @@ +findBy(array()) + ->sort(array('date' => 'desc')) + ->limit(1) + ->getSingleResult(); + } + + public function findManyComments() + { + return $this->findBy(array()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Document.php b/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Document.php new file mode 100644 index 00000000..d676f54c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Document.php @@ -0,0 +1,19 @@ + + */ + +/** + * @ODM\Document(db="doctrine_odm_tests", collection="accounts", repositoryClass="Documents\CustomRepository\Repository") + */ +class Document { + /** + * @ODM\Id + */ + protected $id; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Repository.php b/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Repository.php new file mode 100644 index 00000000..ffbc9fd5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CustomRepository/Repository.php @@ -0,0 +1,10 @@ + + */ +class Repository extends DocumentRepository {} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/CustomUser.php b/vendor/doctrine/mongodb-odm/tests/Documents/CustomUser.php new file mode 100644 index 00000000..652d071c --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/CustomUser.php @@ -0,0 +1,62 @@ +id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } + + public function setAccount(Account $account) + { + $this->account = $account; + $this->account->setUser($this); + } + + public function getAccount() + { + return $this->account; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Customer.php b/vendor/doctrine/mongodb-odm/tests/Documents/Customer.php new file mode 100644 index 00000000..58905423 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Customer.php @@ -0,0 +1,23 @@ +name = $name; + $this->projects = null === $projects ? new ArrayCollection() : $projects; + } + + public function getId() + { + return $this->id; + } + + public function getProjects() + { + return $this->projects; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/ConfigurableProduct.php b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/ConfigurableProduct.php new file mode 100644 index 00000000..9394bae3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/ConfigurableProduct.php @@ -0,0 +1,143 @@ +setName($name); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $name = (string) $name; + if (empty($name)) { + throw new \InvalidArgumentException('Product name cannot be empty'); + } + $this->name = $name; + return $this; + } + + public function getOptions() + { + return $this->options; + } + + /** + * @param string|Option $name + * @param float|null $price + * @param StockItem|null $item + */ + public function addOption($name, $price = null, StockItem $item = null) + { + if ( ! $name instanceof Option) { + $name = (string) $name; + if (empty($name)) { + throw new \InvalidArgumentException('option name cannot be empty'); + } + $name = new Option($name, $price, $item); + unset($price, $item); + } + if (null !== $this->_findOption($name->getName()) + || in_array($name->getStockItem(), $this->_getStockItems(), true)) { + throw new \InvalidArgumentException('cannot add option with the same name twice'); + } + $this->options[] = $name; + } + + public function getOption($name) + { + return $this->_findOption($name); + } + + public function removeOption($name) + { + if(null === ($option = $this->_findOption($name))) { + throw new \InvalidArgumentException('option ' . $name . ' doesn\'t exist'); + } + if ($this->options instanceof \Doctrine\Common\Collections\Collection) { + $index = $this->options->indexOf($option); + } else { + $index = array_search($option, $this->options); + } + unset($this->options[$index]); + return $this; + } + + public function hasOption($name) + { + return null !== $this->_findOption($name); + } + + public function selectOption($name) + { + $option = $this->_findOption($name); + if ( ! isset($option)) { + throw new \InvalidArgumentException('specified option: ' . $name . ' doesn\'t exist'); + } + $this->selectedOption = $option; + return $this; + } + + protected function _findOption($name) + { + foreach ($this->options as $option) { + if ($name == $option->getName()) { + return $option; + } + } + return null; + } + + public function getPrice() + { + return isset($this->selectedOption) ? + $this->selectedOption->getPrice() : null; + } + + protected function _getStockItems() + { + $stockItems = array(); + foreach ($this->getOptions() as $option) { + $stockItems[] = $option->getStockItem(); + } + return $stockItems; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Currency.php b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Currency.php new file mode 100644 index 00000000..d2d6b66d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Currency.php @@ -0,0 +1,79 @@ +name = $name; + $this->setMultiplier($multiplier); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function getMultiplier() + { + return $this->multiplier; + } + + public function setMultiplier($multiplier) + { + $multiplier = (float) $multiplier; + if (empty($multiplier) || $multiplier <= 0) { + throw new \InvalidArgumentException( + 'currency multiplier must be a positive float number' + ); + } + $this->multiplier = $multiplier; + } + + public static function getAll() + { + return array( + self::USD, + self::EURO, + self::JPN, + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Money.php b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Money.php new file mode 100644 index 00000000..47629fcb --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Money.php @@ -0,0 +1,46 @@ +amount = $amount; + $this->setCurrency($currency); + } + + public function getAmount() + { + return $this->amount * $this->getCurrency()->getMultiplier(); + } + + public function getCurrency() + { + return $this->currency; + } + + public function setCurrency(Currency $currency) + { + $this->currency = $currency; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Option.php b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Option.php new file mode 100644 index 00000000..4c0a839b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/Option.php @@ -0,0 +1,77 @@ +name = (string) $name; + if (empty($this->name)) { + throw new \InvalidArgumentException('option name cannot be empty'); + } + $this->money = $money; + if (empty($this->money)) { + throw new \InvalidArgumentException('option price cannot be empty'); + } + $this->stockItem = $stockItem; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return float + */ + public function getPrice($object = false) + { + if (true === $object) { + return $this->money; + } + return $this->money->getAmount(); + } + + /** + * @return StockItem + */ + public function getStockItem() + { + return $this->stockItem; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/StockItem.php b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/StockItem.php new file mode 100644 index 00000000..6c451796 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Ecommerce/StockItem.php @@ -0,0 +1,81 @@ +id; + } + + public function __construct($name = null, $cost = null, $inventory = null) + { + if (null !== $name) { + $this->setName($name); + } + if (null !== $cost) { + $this->setCost($cost); + } + if (null !== $inventory) { + $this->setInventory($inventory); + } + } + + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + public function getName() + { + return $this->name; + } + + public function setCost(Money $cost) + { + $this->cost = $cost; + } + + public function getCost() + { + return $this->cost->getAmount(); + } + + public function setInventory($inventory) + { + $this->inventory = (int) $inventory; + return $this; + } + + public function getInventory() + { + return $this->inventory; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Employee.php b/vendor/doctrine/mongodb-odm/tests/Documents/Employee.php new file mode 100644 index 00000000..85c13da9 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Employee.php @@ -0,0 +1,23 @@ +manager; + } + + public function setManager($val) + { + $this->manager = $val; + return $this; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Event.php b/vendor/doctrine/mongodb-odm/tests/Documents/Event.php new file mode 100644 index 00000000..95dd337b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Event.php @@ -0,0 +1,56 @@ +id; + } + + public function setUser(User $user) + { + $this->user = $user; + } + + public function getUser() + { + return $this->user; + } + + public function getTitle() + { + return $this->title; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getType() + { + return $this->type; + } + + public function setType($type) + { + $this->type = $type; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Feature.php b/vendor/doctrine/mongodb-odm/tests/Documents/Feature.php new file mode 100644 index 00000000..134dc445 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Feature.php @@ -0,0 +1,25 @@ +name = $name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/File.php b/vendor/doctrine/mongodb-odm/tests/Documents/File.php new file mode 100644 index 00000000..762fab71 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/File.php @@ -0,0 +1,50 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getFile() + { + return $this->file; + } + + public function setFile($file) + { + $this->file = $file; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/ForumAvatar.php b/vendor/doctrine/mongodb-odm/tests/Documents/ForumAvatar.php new file mode 100644 index 00000000..7bd93ac4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/ForumAvatar.php @@ -0,0 +1,12 @@ +id; + } + + public function getUsername() + { + return $this->username; + } + + public function getAvatar() + { + return $this->avatar; + } + + public function setAvatar(ForumAvatar $avatar) + { + $this->avatar = $avatar; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/FriendUser.php b/vendor/doctrine/mongodb-odm/tests/Documents/FriendUser.php new file mode 100644 index 00000000..c428b980 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/FriendUser.php @@ -0,0 +1,38 @@ +name = $name; + $this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection(); + $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection(); + } + + public function addFriend(FriendUser $user) + { + $user->friendsWithMe[] = $this; + $this->myFriends[] = $user; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/AlsoLoad.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/AlsoLoad.php new file mode 100644 index 00000000..f5441c00 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/AlsoLoad.php @@ -0,0 +1,65 @@ +firstName = $e[0]; + $this->lastName = $e[1]; + } + + /** @ODM\AlsoLoad({"test1", "test2"}) */ + public function populateTest($test) + { + $this->test = $test; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Embedded.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Embedded.php new file mode 100644 index 00000000..dc07fb6e --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Embedded.php @@ -0,0 +1,12 @@ +preRemove = true; + } + + /** @ODM\PostRemove */ + public function onPostRemove() + { + $this->postRemove = true; + } + + /** @ODM\PreLoad */ + public function onPreLoad() + { + $this->preLoad = true; + } + + /** @ODM\PostLoad */ + public function onPostLoad() + { + $this->postLoad = true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel2.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel2.php new file mode 100644 index 00000000..b05d13a6 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/EmbeddedTestLevel2.php @@ -0,0 +1,41 @@ +preRemove = true; + } + + /** @ODM\PostRemove */ + public function onPostRemove() + { + $this->postRemove = true; + } + + /** @ODM\PreLoad */ + public function onPreLoad() + { + $this->preLoad = true; + } + + /** @ODM\PostLoad */ + public function onPostLoad() + { + $this->postLoad = true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/FavoritesUser.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/FavoritesUser.php new file mode 100644 index 00000000..c5d4d045 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/FavoritesUser.php @@ -0,0 +1,85 @@ +favorite = $favorite; + } + + public function getFavorite() + { + return $this->favorite; + } + + public function setEmbed($embed) + { + $this->embed = $embed; + } + + public function getEmbed() + { + return $this->embed; + } + + public function embed($document) + { + $this->embedded[] = $document; + } + + public function getEmbedded() + { + return $this->embedded; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function addFavorite($favorite) + { + $this->favorites[] = $favorite; + } + + public function getFavorites() + { + return $this->favorites; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotAnnotatedDocument.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotAnnotatedDocument.php new file mode 100644 index 00000000..f5ad8455 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/NotAnnotatedDocument.php @@ -0,0 +1,17 @@ +name; + } + + public function setName($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSellable.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSellable.php new file mode 100644 index 00000000..f980aba5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSellable.php @@ -0,0 +1,27 @@ +product; + } + + public function getSeller() + { + return $this->seller; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSeller.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSeller.php new file mode 100644 index 00000000..61f9b276 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/PreUpdateTestSeller.php @@ -0,0 +1,30 @@ +name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** @ODM\PreUpdate */ + public function preUpdate() + { + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Reference.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Reference.php new file mode 100644 index 00000000..3fe880c2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Reference.php @@ -0,0 +1,15 @@ +level1 = new ArrayCollection(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayCollectionLevel1.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayCollectionLevel1.php new file mode 100644 index 00000000..baebced2 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayCollectionLevel1.php @@ -0,0 +1,21 @@ +level2 = new ArrayCollection(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayLevel0.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayLevel0.php new file mode 100644 index 00000000..93a8363d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/Ticket/MODM160/EmbedManyInArrayLevel0.php @@ -0,0 +1,18 @@ +id; + } + + /** + * @return Documents\Functional\VirtualHostDirective + */ + public function getVHostDirective() + { + if (!$this->vhostDirective) { + $this->vhostDirective = new VirtualHostDirective('VirtualHost', '*:80'); + } + return $this->vhostDirective; + } + + public function setVHostDirective($value) + { + $this->vhostDirective = $value; + + return $this; + } + +} + diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Functional/VirtualHostDirective.php b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/VirtualHostDirective.php new file mode 100644 index 00000000..24fc3d0f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Functional/VirtualHostDirective.php @@ -0,0 +1,118 @@ +name = $name; + $this->value = $value; + } + + public function __toString() + { + return $this->name . ' ' . $this->value; + } + + public function getRecId() + { + return $this->recId; + } + + public function setRecId($value=null) + { + if (!$value) + $value = uniqid(); + + $this->recId = $value; + } + + /* Added automatically 2010-08-03 */ + + public function getName() + { + return $this->name; + } + + public function setName($value) + { + $this->name = $value; + } + + public function getValue() + { + return $this->value; + } + + public function setValue($value) + { + $this->value = $value; + } + + public function getDirectives() + { + if (!$this->directives) + $this->directives = new \Doctrine\Common\Collections\ArrayCollection(array()); + return $this->directives; + } + + public function setDirectives($value) + { + $this->directives = $value; + + return $this; + } + + public function addDirective(VirtualHostDirective $d) + { + $this->getDirectives()->add($d); + + return $this; + } + + /** + * + * @param string $name + * @return VirtualHostDirective + */ + public function hasDirective($name) + { + foreach ($this->getDirectives() as $d) { + if ($d->getName() == $name) { + return $d; + } + } + return null; + } + + + public function getDirective($name) + { + $d = $this->hasDirective($name); + return $d; + } + + public function removeDirective(VirtualHostDirective $d) + { + $this->getDirectives()->removeElement($d); + + return $this; + } + +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Group.php b/vendor/doctrine/mongodb-odm/tests/Documents/Group.php new file mode 100644 index 00000000..9a53d6d5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Group.php @@ -0,0 +1,36 @@ +name = $name; + } + + public function getId() + { + return $this->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/mongodb-odm/tests/Documents/GuestServer.php b/vendor/doctrine/mongodb-odm/tests/Documents/GuestServer.php new file mode 100644 index 00000000..c42a9cd3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/GuestServer.php @@ -0,0 +1,10 @@ +name = $name; + $this->description = $description; + } + + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Manager.php b/vendor/doctrine/mongodb-odm/tests/Documents/Manager.php new file mode 100644 index 00000000..5229b5a5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Manager.php @@ -0,0 +1,22 @@ +projects; + } + + public function addProject(Project $project) + { + $this->projects[] = $project; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Message.php b/vendor/doctrine/mongodb-odm/tests/Documents/Message.php new file mode 100644 index 00000000..f530d91f --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Message.php @@ -0,0 +1,35 @@ +name = $name; + } + + public function getId() + { + return $this->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/mongodb-odm/tests/Documents/OtherSubProject.php b/vendor/doctrine/mongodb-odm/tests/Documents/OtherSubProject.php new file mode 100644 index 00000000..7c952b46 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/OtherSubProject.php @@ -0,0 +1,10 @@ +phonenumber = $phonenumber; + $this->lastCalledBy = $lastCalledBy; + } + + public function getPhonenumber() + { + return $this->phonenumber; + } + + public function setPhonenumber($phonenumber) + { + $this->phonenumber = $phonenumber; + } + + public function getLastCalledBy() + { + return $this->lastCalledBy; + } + + public function setLastCalledBy(User $lastCalledBy = null) + { + $this->lastCalledBy = $lastCalledBy; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Product.php b/vendor/doctrine/mongodb-odm/tests/Documents/Product.php new file mode 100644 index 00000000..8f654549 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Product.php @@ -0,0 +1,32 @@ +name = $name; + $this->features = new \Doctrine\Common\Collections\ArrayCollection(); + } + + public function addFeature(Feature $feature) + { + $feature->product = $this; + $this->features[] = $feature; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Profile.php b/vendor/doctrine/mongodb-odm/tests/Documents/Profile.php new file mode 100644 index 00000000..df417be4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Profile.php @@ -0,0 +1,57 @@ +profileId; + } + + public function setFirstName($firstName) + { + $this->firstName = $firstName; + } + + public function getFirstName() + { + return $this->firstName; + } + + public function setLastName($lastName) + { + $this->lastName = $lastName; + } + + public function getLastName() + { + return $this->lastName; + } + + public function setImage(File $image) + { + $this->image = $image; + } + + public function getImage() + { + return $this->image; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Project.php b/vendor/doctrine/mongodb-odm/tests/Documents/Project.php new file mode 100644 index 00000000..6d623f5b --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Project.php @@ -0,0 +1,71 @@ +name = $name; + $this->subProjects = $subProjects ? $subProjects : new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function setAddress(Address $address) + { + $this->address = $address; + } + + public function getAddress() + { + return $this->address; + } + + public function setSubProjects(Collection $subProjects) + { + $this->subProjects = $subProjects; + } + + public function getSubProjects() + { + return $this->subProjects; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Server.php b/vendor/doctrine/mongodb-odm/tests/Documents/Server.php new file mode 100644 index 00000000..760ae106 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Server.php @@ -0,0 +1,23 @@ +user = $user; + } + + public function getUser() + { + return $this->user; + } + + public function addUser($user) + { + $this->users[] = $user; + } + + public function getUsers() + { + return $this->users; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Song.php b/vendor/doctrine/mongodb-odm/tests/Documents/Song.php new file mode 100644 index 00000000..0b30e6d4 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Song.php @@ -0,0 +1,27 @@ +name = $name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/SpecialUser.php b/vendor/doctrine/mongodb-odm/tests/Documents/SpecialUser.php new file mode 100644 index 00000000..959526d5 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/SpecialUser.php @@ -0,0 +1,22 @@ +rules = $rules; + } + + public function getRules() + { + return $this->rules; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Strategy.php b/vendor/doctrine/mongodb-odm/tests/Documents/Strategy.php new file mode 100644 index 00000000..ad292e9a --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Strategy.php @@ -0,0 +1,21 @@ +issues; + } + + public function setIssues(Collection $issues) + { + $this->issues = $issues; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Tag.php b/vendor/doctrine/mongodb-odm/tests/Documents/Tag.php new file mode 100644 index 00000000..3c726481 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Tag.php @@ -0,0 +1,28 @@ +name = $name; + } + + public function addBlogPost(BlogPost $blogPost) + { + $this->blogPosts[] = $blogPost; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/Task.php b/vendor/doctrine/mongodb-odm/tests/Documents/Task.php new file mode 100644 index 00000000..c5d89980 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/Task.php @@ -0,0 +1,35 @@ +name = $name; + } + + public function getId() + { + return $this->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/mongodb-odm/tests/Documents/User.php b/vendor/doctrine/mongodb-odm/tests/Documents/User.php new file mode 100644 index 00000000..97ccf0a6 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/User.php @@ -0,0 +1,283 @@ +phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groups = array(); + $this->sortedGroups = array(); + $this->sortedGroupsAsc = array(); + $this->posts = array(); + $this->createdAt = new \DateTime(); + } + + public function setId($id) + { + $this->id = $id; + } + + public function getLogs() + { + return $this->logs; + } + + public function setLogs($logs) + { + $this->logs = $logs; + } + + public function log($log) + { + $this->logs[] = $log; + } + + public function getId() + { + return $this->id; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } + + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + } + + public function getCreatedAt() + { + return $this->createdAt; + } + + public function getAddress() + { + return $this->address; + } + + public function setAddress(Address $address = null) + { + $this->address = $address; + } + + public function removeAddress() + { + $this->address = null; + } + + public function setProfile(Profile $profile) + { + $this->profile = $profile; + } + + public function getProfile() + { + return $this->profile; + } + + public function setAccount(Account $account) + { + $this->account = $account; + $this->account->setUser($this); + } + + public function getAccount() + { + return $this->account; + } + + public function getPhonenumbers() + { + return $this->phonenumbers; + } + + public function addPhonenumber(Phonenumber $phonenumber) + { + $this->phonenumbers[] = $phonenumber; + } + + public function getSortedAscGroups() + { + return $this->sortedAscGroups; + } + + public function getSortedDescGroups() + { + return $this->sortedDescGroups; + } + + public function getGroups() + { + return $this->groups; + } + + public function setGroups($groups) + { + $this->groups = $groups; + } + + public function addGroup(Group $group) + { + $this->groups[] = $group; + } + + public function removeGroup($name) + { + foreach ($this->groups as $key => $group) { + if ($group->getName() === $name) { + unset($this->groups[$key]); + return true; + } + } + return false; + } + + public function getHits() + { + return $this->hits; + } + + public function setHits($hits) + { + $this->hits = $hits; + } + + public function getCount() + { + return $this->count; + } + + public function setCount($count) + { + $this->count = $count; + } + + public function getSimpleReferenceOneInverse() + { + return $this->simpleReferenceOneInverse; + } + + public function getSimpleReferenceManyInverse() + { + return $this->simpleReferenceManyInverse; + } + + public function incrementCount($num = null) + { + if ($num === null) { + $this->count++; + } else { + $this->count = $this->count + $num; + } + } + + public function setPosts($posts) + { + $this->posts = $posts; + } + + public function addPost(BlogPost $post) + { + $this->posts[] = $post; + } + + public function removePost($id) + { + foreach ($this->posts as $key => $post) { + if ($post->getId() === $id) { + unset($this->groups[$key]); + return true; + } + } + return false; + } + + public function getPosts() + { + return $this->posts; + } + +} diff --git a/vendor/doctrine/mongodb-odm/tests/Documents/UserUpsert.php b/vendor/doctrine/mongodb-odm/tests/Documents/UserUpsert.php new file mode 100644 index 00000000..214047db --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Documents/UserUpsert.php @@ -0,0 +1,33 @@ + + */ +class NativePhpunitTask extends Task +{ + private $test; + private $testfile; + private $testdirectory; + private $configuration = null; + private $coverageClover = null; + private $junitlogfile = null; + private $haltonfailure = true; + private $haltonerror = true; + + public function setTestdirectory($directory) { + $this->testdirectory = $directory; + } + + public function setTest($test) { + $this->test = $test; + } + + public function setTestfile($testfile) { + $this->testfile = $testfile; + } + + public function setJunitlogfile($junitlogfile) { + if (strlen($junitlogfile) == 0) { + $junitlogfile = NULL; + } + + $this->junitlogfile = $junitlogfile; + } + + public function setConfiguration($configuration) { + if (strlen($configuration) == 0) { + $configuration = NULL; + } + + $this->configuration = $configuration; + } + + public function setCoverageClover($coverageClover) { + if (strlen($coverageClover) == 0) { + $coverageClover = NULL; + } + + $this->coverageClover = $coverageClover; + } + + public function setHaltonfailure($haltonfailures) { + $this->haltonfailure = $haltonfailures; + } + + public function setHaltonerror($haltonerrors) { + $this->haltonerror = $haltonerrors; + } + + public function init() + { + require_once "PHPUnit/Runner/Version.php"; + $version = PHPUnit_Runner_Version::id(); + + if (version_compare($version, '3.4.0') < 0) + { + throw new BuildException("NativePHPUnitTask requires PHPUnit version >= 3.2.0", $this->getLocation()); + } + + require_once 'PHPUnit/Util/Filter.php'; + + // point PHPUnit_MAIN_METHOD define to non-existing method + if (!defined('PHPUnit_MAIN_METHOD')) + { + define('PHPUnit_MAIN_METHOD', 'PHPUnitTask::undefined'); + } + } + + public function main() + { + if (!is_dir(realpath($this->testdirectory))) { + throw new BuildException("NativePHPUnitTask requires a Test Directory path given, '".$this->testdirectory."' given."); + } + set_include_path(realpath($this->testdirectory) . PATH_SEPARATOR . get_include_path()); + + $printer = new NativePhpunitPrinter(); + + $arguments = array( + 'configuration' => $this->configuration, + 'coverageClover' => $this->coverageClover, + 'junitLogfile' => $this->junitlogfile, + 'printer' => $printer, + ); + + require_once "PHPUnit/TextUI/TestRunner.php"; + $runner = new PHPUnit_TextUI_TestRunner(); + $suite = $runner->getTest($this->test, $this->testfile, true); + + try { + $result = $runner->doRun($suite, $arguments); + /* @var $result PHPUnit_Framework_TestResult */ + + if ( ($this->haltonfailure && $result->failureCount() > 0) || ($this->haltonerror && $result->errorCount() > 0) ) { + throw new BuildException("PHPUnit: ".$result->failureCount()." Failures and ".$result->errorCount()." Errors, ". + "last failure message: ".$printer->getMessages()); + } + + $this->log("PHPUnit Success: ".count($result->passed())." tests passed, no ". + "failures (".$result->skippedCount()." skipped, ".$result->notImplementedCount()." not implemented)"); + + // Hudson for example doesn't like the backslash in class names + if (file_exists($this->coverageClover)) { + $this->log("Generated Clover Coverage XML to: ".$this->coverageClover); + $content = file_get_contents($this->coverageClover); + $content = str_replace("\\", ".", $content); + file_put_contents($this->coverageClover, $content); + unset($content); + } + + } catch(\Exception $e) { + throw new BuildException("NativePhpunitTask failed: ".$e->getMessage()); + } + } +} + +class NativePhpunitPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + private $_messages = array(); + + public function write($buffer) + { + // do nothing + } + + public function getMessages() + { + return $this->_messages; + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->_messages[] = "Test ERROR: ".$test->getName().": ".$e->getMessage(); + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->_messages[] = "Test FAILED: ".$test->getName().": ".$e->getMessage(); + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + + } + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + + } + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/Stubs/DocumentManager.php b/vendor/doctrine/mongodb-odm/tests/Stubs/DocumentManager.php new file mode 100644 index 00000000..afda4d52 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/Stubs/DocumentManager.php @@ -0,0 +1,42 @@ + + */ +class DocumentManager extends BaseDocumentManager +{ + + protected $classMetadatas = array(); + + private $_eventManager; + + public function __construct() + { + $this->_eventManager = new EventManager(); + } + + public function getEventManager() + { + return $this->_eventManager; + } + + public function setClassMetadata($className, ClassMetadata $class) + { + $this->classMetadatas[$className] = $class; + } + + public function getClassMetadata($className) + { + if ( ! isset($this->classMetadatas[$className])) { + throw new \InvalidArgumentException('Metadata for class ' . $className . ' doesn\'t exist, try calling ->setClassMetadata() first'); + } + return $this->classMetadatas[$className]; + } + +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb-odm/tests/bootstrap.php b/vendor/doctrine/mongodb-odm/tests/bootstrap.php new file mode 100644 index 00000000..8a761fed --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tests/bootstrap.php @@ -0,0 +1,13 @@ +add('Doctrine\ODM\MongoDB\Tests', __DIR__ . '/../tests'); +$loader->add('Documents', __DIR__); +$loader->add('Stubs', __DIR__); + +\Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver::registerAnnotationClasses(); diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Account.php b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Account.php new file mode 100644 index 00000000..d74673b7 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Account.php @@ -0,0 +1,40 @@ +name = $name; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Address.php b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Address.php new file mode 100644 index 00000000..c29b3afd --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Address.php @@ -0,0 +1,61 @@ +street; + } + + public function setStreet($street) + { + $this->street = $street; + } + + public function getCity() + { + return $this->city; + } + + public function setCity($city) + { + $this->city = $city; + } + + public function getState() + { + return $this->state; + } + + public function setState($state) + { + $this->state = $state; + } + + public function getPostalCode() + { + return $this->postalCode; + } + + public function setPostalCode($postalCode) + { + $this->postalCode = $postalCode; + } +} diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Phonenumber.php b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Phonenumber.php new file mode 100644 index 00000000..d577918d --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/Phonenumber.php @@ -0,0 +1,32 @@ +phonenumber = $phonenumber; + } + + public function setPhonenumber($phonenumber) + { + $this->phonenumber = $phonenumber; + } + + public function getPhonenumber() + { + return $this->phonenumber; + } + + public function __toString() + { + return $this->phonenumber; + } +} diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/User.php b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/User.php new file mode 100644 index 00000000..7919fb13 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/User.php @@ -0,0 +1,87 @@ +id; + } + + public function setUsername($username) + { + $this->username = $username; + } + + public function getUsername() + { + return $this->username; + } + + public function setPassword($password) + { + $this->password = md5($password); + } + + public function checkPassword($password) + { + return $this->password === md5($password) ? true : false; + } + + public function setAddress(Address $address) + { + $this->address = $address; + } + + public function getAddress() + { + return $this->address; + } + + public function setAccount(Account $account) + { + $this->account = $account; + } + + public function getAccount() + { + return $this->account; + } + + public function addPhonenumber(Phonenumber $phonenumber) + { + $this->phonenumbers[] = $phonenumber; + } + + public function getPhonenumbers() + { + return $this->phonenumbers; + } + + public function __toString() + { + return $this->username; + } +} diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/UserRepository.php b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/UserRepository.php new file mode 100644 index 00000000..0bd203e3 --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/Documents/UserRepository.php @@ -0,0 +1,15 @@ +register(); + +$config = new Configuration(); + +$config->setProxyDir(__DIR__ . '/Proxies'); +$config->setProxyNamespace('Proxies'); + +$config->setHydratorDir(__DIR__ . '/Hydrators'); +$config->setHydratorNamespace('Hydrators'); + +$config->setDefaultDB('doctrine_odm_sandbox'); + +// $config->setLoggerCallable(function(array $log) { print_r($log); }); +// $config->setMetadataCacheImpl(new Doctrine\Common\Cache\ApcCache()); + +$config->setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents')); + +$dm = DocumentManager::create(new Connection(), $config); diff --git a/vendor/doctrine/mongodb-odm/tools/sandbox/index.php b/vendor/doctrine/mongodb-odm/tools/sandbox/index.php new file mode 100644 index 00000000..8f5ac3ba --- /dev/null +++ b/vendor/doctrine/mongodb-odm/tools/sandbox/index.php @@ -0,0 +1,10 @@ + $helper) { + $helperSet->set($helper, $name); +} + +$cli = new Application('Doctrine ODM MongoDB Command Line Interface', Doctrine\ODM\MongoDB\Version::VERSION); +$cli->setCatchExceptions(true); +$cli->setHelperSet($helperSet); +$cli->addCommands(array( + new \Doctrine\ODM\MongoDB\Tools\Console\Command\QueryCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\GenerateDocumentsCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\GenerateRepositoriesCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\GenerateProxiesCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\GenerateHydratorsCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\Schema\CreateCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\Schema\DropCommand(), + new \Doctrine\ODM\MongoDB\Tools\Console\Command\Schema\UpdateCommand(), +)); +$cli->run(); diff --git a/vendor/doctrine/mongodb/.gitignore b/vendor/doctrine/mongodb/.gitignore new file mode 100644 index 00000000..92d47863 --- /dev/null +++ b/vendor/doctrine/mongodb/.gitignore @@ -0,0 +1,5 @@ +phpunit.xml +vendor +composer.lock +composer.phar + diff --git a/vendor/doctrine/mongodb/.travis.yml b/vendor/doctrine/mongodb/.travis.yml new file mode 100644 index 00000000..380b61fd --- /dev/null +++ b/vendor/doctrine/mongodb/.travis.yml @@ -0,0 +1,21 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + +matrix: + allow_failures: + - php: 5.5 + +env: + - MONGO_VERSION=1.2.12 + - MONGO_VERSION=1.3.2 + - MONGO_VERSION=1.3.7 + +services: mongodb + +before_script: + - pecl -q install -f mongo-${MONGO_VERSION} && echo "extension=mongo.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` + - composer install --dev --no-interaction --prefer-source diff --git a/vendor/doctrine/mongodb/LICENSE b/vendor/doctrine/mongodb/LICENSE new file mode 100644 index 00000000..4edf3e9c --- /dev/null +++ b/vendor/doctrine/mongodb/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2006-2012 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/mongodb/README.markdown b/vendor/doctrine/mongodb/README.markdown new file mode 100644 index 00000000..c621dca9 --- /dev/null +++ b/vendor/doctrine/mongodb/README.markdown @@ -0,0 +1,12 @@ +# Doctrine MongoDB + +[![Build Status](https://secure.travis-ci.org/doctrine/mongodb.png)](http://travis-ci.org/doctrine/mongodb) + +The Doctrine MongoDB project is a library that provides a wrapper around the native PHP Mongo PECL extension to provide additional functionality. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/index.html) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/MODM) +* [Downloads](http://github.com/doctrine/mongodb/downloads) \ No newline at end of file diff --git a/vendor/doctrine/mongodb/composer.json b/vendor/doctrine/mongodb/composer.json new file mode 100644 index 00000000..f1fa5a5c --- /dev/null +++ b/vendor/doctrine/mongodb/composer.json @@ -0,0 +1,22 @@ +{ + "name": "doctrine/mongodb", + "type": "library", + "description": "Doctrine MongoDB Abstraction Layer", + "keywords": ["database", "persistence", "mongodb"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + { "name": "Bulat Shakirzyanov", "email": "mallluhuct@gmail.com" }, + { "name": "Kris Wallsmith", "email": "kris.wallsmith@gmail.com" }, + { "name": "Jonathan H. Wage", "email": "jonwage@gmail.com" }, + { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" } + ], + "require": { + "php": ">=5.3.2", + "ext-mongo": ">=1.2.12,<1.5-dev", + "doctrine/common": ">=2.1.0,<2.5-dev" + }, + "autoload": { + "psr-0": { "Doctrine\\MongoDB": "lib/" } + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/ArrayIterator.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/ArrayIterator.php new file mode 100644 index 00000000..59029ff5 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/ArrayIterator.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\MongoDB; + +use ArrayAccess; + +/** + * ArrayIterator + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @since 1.0 + * @author Jonathan H. Wage + */ +class ArrayIterator implements Iterator, ArrayAccess +{ + private $elements; + + public function __construct(array $elements = array()) + { + $this->elements = $elements; + } + + public function first() + { + return reset($this->elements); + } + + public function last() + { + return end($this->elements); + } + + public function key() + { + return key($this->elements); + } + + public function next() + { + next($this->elements); + } + + public function current() + { + return current($this->elements); + } + + public function count() + { + return count($this->elements); + } + + public function rewind() + { + reset($this->elements); + } + + public function reset() + { + reset($this->elements); + } + + public function valid() + { + return current($this->elements) !== false; + } + + public function offsetSet($offset, $value) + { + $this->elements[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->elements[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->elements[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->elements[$offset]) ? $this->elements[$offset] : null; + } + + public function toArray() + { + return $this->elements; + } + + public function getSingleResult() + { + $result = null; + $this->valid() ?: $this->next(); + if ($this->valid()) { + $result = $this->current(); + } + $this->reset(); + return $result ? $result : null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Collection.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Collection.php new file mode 100644 index 00000000..1da49d5f --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Collection.php @@ -0,0 +1,699 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\Common\EventManager; +use Doctrine\MongoDB\Event\DistinctEventArgs; +use Doctrine\MongoDB\Event\EventArgs; +use Doctrine\MongoDB\Event\GroupEventArgs; +use Doctrine\MongoDB\Event\MapReduceEventArgs; +use Doctrine\MongoDB\Event\NearEventArgs; +use Doctrine\MongoDB\Event\UpdateEventArgs; +use Doctrine\MongoDB\Util\ReadPreference; + +/** + * Wrapper for the PHP MongoCollection class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +class Collection +{ + /** + * The Doctrine Connection object. + * + * @var Doctrine\MongoDB\Connection + */ + protected $connection; + + /** + * The name of the collection. + * + * @var string $name + */ + protected $name; + + /** + * The Database instance this collection belongs to. + * + * @var Database + */ + protected $database; + + /** + * The event manager that is the central point of the event system. + * + * @var Doctrine\Common\EventManager + */ + protected $eventManager; + + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * Number of times to retry queries. + * + * @var mixed + */ + protected $numRetries; + + /** + * Create a new MongoCollection instance that wraps a PHP MongoCollection instance + * for a given ClassMetadata instance. + * + * @param Connection $connection The Doctrine Connection instance. + * @param string $name The name of the collection. + * @param Database $database The Database instance. + * @param EventManager $evm The EventManager instance. + * @param string $cmd Mongo cmd character. + * @param boolean|integer $numRetries Number of times to retry queries. + */ + public function __construct(Connection $connection, $name, Database $database, EventManager $evm, $cmd, $numRetries = 0) + { + $this->connection = $connection; + $this->name = $name; + $this->database = $database; + $this->eventManager = $evm; + $this->cmd = $cmd; + $this->numRetries = (integer) $numRetries; + } + + public function getName() + { + return $this->name; + } + + /** + * Returns the wrapped MongoCollection instance. + * + * @return \MongoCollection + */ + public function getMongoCollection() + { + return $this->database->getMongoDB()->selectCollection($this->name); + } + + /** + * Gets the database this collection belongs to. + * + * @return Database $database + */ + public function getDatabase() + { + return $this->database; + } + + public function getIndexInfo() + { + return $this->getMongoCollection()->getIndexInfo(); + } + + /** + * Check if a given field name is indexed in mongodb. + * + * @param string $fieldName + * @return bool + */ + public function isFieldIndexed($fieldName) + { + $indexes = $this->getIndexInfo(); + foreach ($indexes as $index) { + if (isset($index['key']) && isset($index['key'][$fieldName])) { + return true; + } + } + return false; + } + + /** + * Creates a new query builder instnce. + * + * @return Query\Builder $qb + */ + public function createQueryBuilder() + { + return new Query\Builder($this->database, $this, $this->cmd); + } + + /** @override */ + public function batchInsert(array &$a, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preBatchInsert)) { + $this->eventManager->dispatchEvent(Events::preBatchInsert, new EventArgs($this, $a)); + } + + $result = $this->doBatchInsert($a, $options); + + if ($this->eventManager->hasListeners(Events::postBatchInsert)) { + $this->eventManager->dispatchEvent(Events::postBatchInsert, new EventArgs($this, $result)); + } + + return $a; + } + + protected function doBatchInsert(array &$a, array $options = array()) + { + return $this->getMongoCollection()->batchInsert($a, $options); + } + + /** @override */ + public function update($query, array $newObj, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preUpdate)) { + $this->eventManager->dispatchEvent(Events::preUpdate, new UpdateEventArgs($this, $query, $newObj)); + } + + $result = $this->doUpdate($query, $newObj, $options); + + if ($this->eventManager->hasListeners(Events::postUpdate)) { + $this->eventManager->dispatchEvent(Events::postUpdate, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doUpdate($query, array $newObj, array $options) + { + if (is_scalar($query)) { + $query = array('_id' => $query); + } + return $this->getMongoCollection()->update($query, $newObj, $options); + } + + public function upsert($query, array $newObj, array $options = array()) + { + $options['upsert'] = true; + return $this->update($query, $newObj, $options); + } + + /** @override */ + public function find(array $query = array(), array $fields = array()) + { + if ($this->eventManager->hasListeners(Events::preFind)) { + $this->eventManager->dispatchEvent(Events::preFind, new EventArgs($this, $query)); + } + + $result = $this->doFind($query, $fields); + + if ($this->eventManager->hasListeners(Events::postFind)) { + $this->eventManager->dispatchEvent(Events::postFind, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doFind(array $query, array $fields) + { + $collection = $this; + $cursor = $this->retry(function() use ($collection, $query, $fields) { + return $collection->getMongoCollection()->find($query, $fields); + }); + return $this->wrapCursor($cursor, $query, $fields); + } + + protected function wrapCursor(\MongoCursor $cursor, $query, $fields) + { + return new Cursor($this->connection, $this, $cursor, $query, $fields, $this->numRetries); + } + + /** @override */ + public function findOne(array $query = array(), array $fields = array()) + { + if ($this->eventManager->hasListeners(Events::preFindOne)) { + $this->eventManager->dispatchEvent(Events::preFindOne, new EventArgs($this, $query)); + } + + $result = $this->doFindOne($query, $fields); + + if ($this->eventManager->hasListeners(Events::postFindOne)) { + $this->eventManager->dispatchEvent(Events::postFindOne, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doFindOne(array $query, array $fields) + { + $collection = $this; + return $this->retry(function() use ($collection, $query, $fields) { + return $collection->getMongoCollection()->findOne($query, $fields); + }); + } + + public function findAndRemove(array $query, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preFindAndRemove)) { + $this->eventManager->dispatchEvent(Events::preFindAndRemove, new EventArgs($this, $query)); + } + + $document = $this->doFindAndRemove($query, $options); + + if ($this->eventManager->hasListeners(Events::postFindAndRemove)) { + $this->eventManager->dispatchEvent(Events::postFindAndRemove, new EventArgs($this, $document)); + } + + return $document; + } + + protected function doFindAndRemove(array $query, array $options = array()) + { + $command = array(); + $command['findandmodify'] = $this->getMongoCollection()->getName(); + $command['query'] = $query; + $command['remove'] = true; + $command = array_merge($command, $options); + + $result = $this->database->command($command); + + return isset($result['value']) ? $result['value'] : null; + } + + public function findAndUpdate(array $query, array $newObj, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preFindAndUpdate)) { + $this->eventManager->dispatchEvent(Events::preFindAndUpdate, new UpdateEventArgs($this, $query, $query)); + } + + $document = $this->doFindAndUpdate($query, $newObj, $options); + + if ($this->eventManager->hasListeners(Events::postFindAndUpdate)) { + $this->eventManager->dispatchEvent(Events::postFindAndUpdate, new EventArgs($this, $document)); + } + + return $document; + } + + protected function doFindAndUpdate(array $query, array $newObj, array $options) + { + $command = array(); + $command['findandmodify'] = $this->getMongoCollection()->getName(); + $command['query'] = $query; + $command['update'] = $newObj; + $command = array_merge($command, $options); + + $result = $this->database->command($command); + return isset($result['value']) ? $result['value'] : null; + } + + public function near(array $near, array $query = array(), array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preNear)) { + $this->eventManager->dispatchEvent(Events::preNear, new NearEventArgs($this, $query, $near)); + } + + $result = $this->doNear($near, $query, $options); + + if ($this->eventManager->hasListeners(Events::postNear)) { + $this->eventManager->dispatchEvent(Events::postNear, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doNear(array $near, array $query, array $options) + { + $command = array(); + $command['geoNear'] = $this->getMongoCollection()->getName(); + $command['near'] = $near; + $command['query'] = $query; + $command = array_merge($command, $options); + + $database = $this->database; + $result = $this->retry(function() use ($database, $command) { + return $database->command($command); + }); + return new ArrayIterator(isset($result['results']) ? $result['results'] : array()); + } + + public function distinct($field, array $query = array(), array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preDistinct)) { + $this->eventManager->dispatchEvent(Events::preDistinct, new DistinctEventArgs($this, $field, $query)); + } + + $result = $this->doDistinct($field, $query, $options); + + if ($this->eventManager->hasListeners(Events::postDistinct)) { + $this->eventManager->dispatchEvent(Events::postDistinct, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doDistinct($field, array $query, array $options) + { + $command = array(); + $command['distinct'] = $this->getMongoCollection()->getName(); + $command['key'] = $field; + $command['query'] = $query; + $command = array_merge($command, $options); + + $database = $this->database; + $result = $this->retry(function() use ($database, $command) { + return $database->command($command); + }); + return new ArrayIterator(isset($result['values']) ? $result['values'] : array()); + } + + public function mapReduce($map, $reduce, array $out = array('inline' => true), array $query = array(), array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preMapReduce)) { + $this->eventManager->dispatchEvent(Events::preMapReduce, new MapReduceEventArgs($this, $map, $reduce, $out, $query)); + } + + $result = $this->doMapReduce($map, $reduce, $out, $query, $options); + + if ($this->eventManager->hasListeners(Events::postMapReduce)) { + $this->eventManager->dispatchEvent(Events::postMapReduce, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doMapReduce($map, $reduce, array $out, array $query, array $options) + { + if (is_string($map)) { + $map = new \MongoCode($map); + } + if (is_string($reduce)) { + $reduce = new \MongoCode($reduce); + } + $command = array(); + $command['mapreduce'] = $this->getMongoCollection()->getName(); + $command['map'] = $map; + $command['reduce'] = $reduce; + $command['query'] = (object) $query; + $command['out'] = $out; + $command = array_merge($command, $options); + + $result = $this->database->command($command); + + if (!$result['ok']) { + throw new \RuntimeException($result['errmsg']); + } + + if (isset($out['inline']) && $out['inline'] === true) { + return new ArrayIterator($result['results']); + } + + return $this->database->selectCollection($result['result'])->find(); + } + + public function count(array $query = array(), $limit = 0, $skip = 0) + { + $collection = $this; + return $this->retry(function() use ($collection, $query, $limit, $skip) { + return $collection->getMongoCollection()->count($query, $limit, $skip); + }); + } + + public function createDBRef(array $a) + { + if ($this->eventManager->hasListeners(Events::preCreateDBRef)) { + $this->eventManager->dispatchEvent(Events::preCreateDBRef, new EventArgs($this, $a)); + } + + $result = $this->doCreateDBRef($a); + + if ($this->eventManager->hasListeners(Events::postCreateDBRef)) { + $this->eventManager->dispatchEvent(Events::postCreateDBRef, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doCreateDBRef(array $a) + { + return $this->getMongoCollection()->createDBRef($a); + } + + public function deleteIndex($keys) + { + return $this->getMongoCollection()->deleteIndex($keys); + } + + public function deleteIndexes() + { + return $this->getMongoCollection()->deleteIndexes(); + } + + public function drop() + { + if ($this->eventManager->hasListeners(Events::preDropCollection)) { + $this->eventManager->dispatchEvent(Events::preDropCollection, new EventArgs($this)); + } + + $result = $this->doDrop(); + + if ($this->eventManager->hasListeners(Events::postDropCollection)) { + $this->eventManager->dispatchEvent(Events::postDropCollection, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doDrop() + { + return $this->getMongoCollection()->drop(); + } + + public function ensureIndex(array $keys, array $options = array()) + { + return $this->getMongoCollection()->ensureIndex($keys, $options); + } + + public function __get($name) + { + return $this->getMongoCollection()->__get($name); + } + + public function getDBRef(array $reference) + { + if ($this->eventManager->hasListeners(Events::preGetDBRef)) { + $this->eventManager->dispatchEvent(Events::preGetDBRef, new EventArgs($this, $reference)); + } + + $result = $this->doGetDBRef($reference); + + if ($this->eventManager->hasListeners(Events::postGetDBRef)) { + $this->eventManager->dispatchEvent(Events::postGetDBRef, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doGetDBRef(array $reference) + { + $collection = $this; + return $this->retry(function() use ($collection, $reference) { + return $collection->getMongoCollection()->getDBRef($reference); + }); + } + + public function group($keys, array $initial, $reduce, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preGroup)) { + $this->eventManager->dispatchEvent(Events::preGroup, new GroupEventArgs($this, $keys, $initial, $reduce)); + } + + $result = $this->doGroup($keys, $initial, $reduce, $options); + + if ($this->eventManager->hasListeners(Events::postGroup)) { + $this->eventManager->dispatchEvent(Events::postGroup, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doGroup($keys, array $initial, $reduce, array $options) + { + if (is_string($reduce)) { + $reduce = new \MongoCode($reduce); + } + + if (isset($options['finalize']) && is_string($options['finalize'])) { + $options['finalize'] = new \MongoCode($options['finalize']); + } + + $collection = $this; + $result = $this->retry(function() use ($collection, $keys, $initial, $reduce, $options) { + /* Version 1.2.11+ of the driver yields an E_DEPRECATED notice if an + * empty array is passed to MongoCollection::group(), as it assumes + * it is the "condition" option's value being passed instead of a + * well-formed options array (the actual deprecated behavior). + */ + return empty($options) + ? $collection->getMongoCollection()->group($keys, $initial, $reduce) + : $collection->getMongoCollection()->group($keys, $initial, $reduce, $options); + }); + return new ArrayIterator($result); + } + + public function insert(array &$a, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preInsert)) { + $this->eventManager->dispatchEvent(Events::preInsert, new EventArgs($this, $a)); + } + + $result = $this->doInsert($a, $options); + + if ($this->eventManager->hasListeners(Events::postInsert)) { + $this->eventManager->dispatchEvent(Events::postInsert, new EventArgs($this, $result)); + } + return $result; + } + + protected function doInsert(array &$a, array $options) + { + $document = $a; + $result = $this->getMongoCollection()->insert($document, $options); + if (isset($document['_id'])) { + $a['_id'] = $document['_id']; + } + return $result; + } + + public function remove(array $query, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preRemove)) { + $this->eventManager->dispatchEvent(Events::preRemove, new EventArgs($this, $query)); + } + + $result = $this->doRemove($query, $options); + + if ($this->eventManager->hasListeners(Events::postRemove)) { + $this->eventManager->dispatchEvent(Events::postRemove, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doRemove(array $query, array $options) + { + return $this->getMongoCollection()->remove($query, $options); + } + + public function save(array &$a, array $options = array()) + { + if ($this->eventManager->hasListeners(Events::preSave)) { + $this->eventManager->dispatchEvent(Events::preSave, new EventArgs($this, $a)); + } + + $result = $this->doSave($a, $options); + + if ($this->eventManager->hasListeners(Events::postSave)) { + $this->eventManager->dispatchEvent(Events::postSave, new EventArgs($this, $result)); + } + + return $result; + } + + protected function doSave(array &$a, array $options) + { + return $this->getMongoCollection()->save($a, $options); + } + + /** + * Set whether secondary read queries are allowed for this collection. + * + * This method wraps setSlaveOkay() for driver versions before 1.3.0. For + * newer drivers, this method wraps setReadPreference() and specifies + * SECONDARY_PREFERRED. + */ + public function setSlaveOkay($ok = true) + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + return $this->getMongoCollection()->setSlaveOkay($ok); + } + + $prevSlaveOkay = $this->getSlaveOkay(); + + if ($ok) { + // Preserve existing tags for non-primary read preferences + $readPref = $this->getMongoCollection()->getReadPreference(); + $tags = !empty($readPref['tagsets']) ? ReadPreference::convertTagSets($readPref['tagsets']) : array(); + $this->getMongoCollection()->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags); + } else { + $this->getMongoCollection()->setReadPreference(\MongoClient::RP_PRIMARY); + } + + return $prevSlaveOkay; + } + + /** + * Get whether secondary read queries are allowed for this collection. + * + * This method wraps getSlaveOkay() for driver versions before 1.3.0. For + * newer drivers, this method considers any read preference other than + * PRIMARY as a true "slaveOkay" value. + */ + public function getSlaveOkay() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + return $this->getMongoCollection()->getSlaveOkay(); + } + + $readPref = $this->getMongoCollection()->getReadPreference(); + + if (is_numeric($readPref['type'])) { + $readPref['type'] = ReadPreference::convertNumericType($readPref['type']); + } + + return \MongoClient::RP_PRIMARY !== $readPref['type']; + } + + public function validate($scanData = false) + { + return $this->getMongoCollection()->validate($scanData); + } + + public function __toString() + { + return $this->getMongoCollection()->__toString(); + } + + protected function retry(\Closure $retry) + { + if ($this->numRetries) { + $firstException = null; + for ($i = 0; $i <= $this->numRetries; $i++) { + try { + return $retry(); + } catch (\MongoException $e) { + if (!$firstException) { + $firstException = $e; + } + if ($i === $this->numRetries) { + throw $firstException; + } + } + } + } else { + return $retry(); + } + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Configuration.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Configuration.php new file mode 100644 index 00000000..fadb907c --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Configuration.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Configuration + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class Configuration +{ + /** + * Array of attributes for this configuration instance. + * + * @var array $attributes + */ + protected $attributes = array( + 'mongoCmd' => '$', + 'retryConnect' => 0, + 'retryQuery' => 0 + ); + + /** + * Set the logger callable. + * + * @param mixed $loggerCallable The logger callable. + */ + public function setLoggerCallable($loggerCallable) + { + $this->attributes['loggerCallable'] = $loggerCallable; + } + + /** + * Gets the logger callable. + * + * @return mixed $loggerCallable The logger callable. + */ + public function getLoggerCallable() + { + return isset($this->attributes['loggerCallable']) ? + $this->attributes['loggerCallable'] : null; + } + + /** + * Get mongodb command prefix - '$' by default + * @return string + */ + public function getMongoCmd() + { + return $this->attributes['mongoCmd']; + } + + /** + * Set mongodb command prefix + * @param string $cmd + */ + public function setMongoCmd($cmd) + { + $this->attributes['mongoCmd'] = $cmd; + } + + /** + * Get number of times to retry connect when errors occur. + * + * @return integer The number of times to retry. + */ + public function getRetryConnect() + { + return $this->attributes['retryConnect']; + } + + /** + * Set number of times to retry connect when errors occur. + * + * @param boolean|integer $retryConnect + */ + public function setRetryConnect($retryConnect) + { + $this->attributes['retryConnect'] = (integer) $retryConnect; + } + + /** + * Get number of times to retry queries when they fail. + * + * @return integer The number of times to retry queries. + */ + public function getRetryQuery() + { + return $this->attributes['retryQuery']; + } + + /** + * Set true/false whether or not to retry connect upon failure or number of times to retry. + * + * @param boolean|integer $retryQuery True/false or number of times to retry queries. + */ + public function setRetryQuery($retryQuery) + { + $this->attributes['retryQuery'] = (integer) $retryQuery; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Connection.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Connection.php new file mode 100644 index 00000000..1640ad34 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Connection.php @@ -0,0 +1,308 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\Common\EventManager, + Doctrine\MongoDB\Event\EventArgs; + +/** + * Wrapper for the PHP MongoClient class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + */ +class Connection +{ + /** + * @var MongoClient $mongo + */ + protected $mongo; + + /** + * @var string $server + */ + protected $server; + + /** + * @var array $options + */ + protected $options = array(); + + /** + * @var Doctrine\MongoDB\Configuration + */ + protected $config; + + /** + * The event manager that is the central point of the event system. + * + * @var Doctrine\Common\EventManager + */ + protected $eventManager; + + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * Create a new MongoClient wrapper instance. + * + * @param mixed $server A string server name, an existing MongoClient or Mongo instance, or null + * @param array $options + */ + public function __construct($server = null, array $options = array(), Configuration $config = null, EventManager $evm = null) + { + if ($server instanceof \MongoClient || $server instanceof \Mongo) { + $this->mongo = $server; + } elseif ($server !== null) { + $this->server = $server; + $this->options = $options; + } + $this->config = $config ? $config : new Configuration(); + $this->eventManager = $evm ? $evm : new EventManager(); + $this->cmd = $this->config->getMongoCmd(); + } + + public function initialize($reinitialize = false) + { + if ($reinitialize === true || $this->mongo === null) { + if ($this->eventManager->hasListeners(Events::preConnect)) { + $this->eventManager->dispatchEvent(Events::preConnect, new EventArgs($this)); + } + + $server = $this->server; + $options = $this->options; + $this->mongo = $this->retry(function() use($server, $options) { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + return new \Mongo($server ?: 'mongodb://localhost:27017', $options); + } + + return new \MongoClient($server ?: 'mongodb://localhost:27017', $options); + }); + + if ($this->eventManager->hasListeners(Events::postConnect)) { + $this->eventManager->dispatchEvent(Events::postConnect, new EventArgs($this)); + } + } + } + + /** + * Returns current server string if one was set. + * + * @return string|null + */ + public function getServer() + { + return $this->server; + } + + /** + * Gets the status of the connection. + * + * @return string + */ + public function getStatus() + { + return $this->mongo->status; + } + + /** + * Checks whether the connection is initialized and connected. + * + * @return boolean + */ + public function isConnected() + { + return $this->mongo !== null && $this->mongo instanceof \Mongo && $this->mongo->connected; + } + + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + public function log(array $log) + { + call_user_func_array($this->config->getLoggerCallable(), array($log)); + } + + /** + * Set the PHP MongoClient instance to wrap. + * + * @param MongoClient $mongo The PHP Mongo instance + */ + public function setMongo($mongo) + { + if ( ! ($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { + throw new \InvalidArgumentException('MongoClient or Mongo instance required'); + } + + $this->mongo = $mongo; + } + + /** + * Returns the PHP Mongo instance being wrapped. + * + * @return MongoClient + */ + public function getMongo() + { + return $this->mongo; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return Doctrine\MongoDB\Configuration + */ + public function getConfiguration() + { + return $this->config; + } + + public function close() + { + $this->initialize(); + return $this->mongo->close(); + } + + public function connect() + { + $this->initialize(); + + $mongo = $this->mongo; + return $this->retry(function() use($mongo) { + return $mongo->connect(); + }); + } + + public function dropDatabase($database) + { + if ($this->eventManager->hasListeners(Events::preDropDatabase)) { + $this->eventManager->dispatchEvent(Events::preDropDatabase, new EventArgs($this, $database)); + } + + $this->initialize(); + $result = $this->mongo->dropDB($database); + + if ($this->eventManager->hasListeners(Events::postDropDatabase)) { + $this->eventManager->dispatchEvent(Events::postDropDatabase, new EventArgs($this, $result)); + } + + return $result; + } + + public function __get($key) + { + $this->initialize(); + return $this->mongo->$key; + } + + public function listDatabases() + { + $this->initialize(); + return $this->mongo->listDBs(); + } + + public function selectCollection($db, $collection) + { + $this->initialize(); + return $this->selectDatabase($db)->selectCollection($collection); + } + + public function selectDatabase($name) + { + if ($this->eventManager->hasListeners(Events::preSelectDatabase)) { + $this->eventManager->dispatchEvent(Events::preSelectDatabase, new EventArgs($this, $name)); + } + + $this->initialize(); + $database = $this->wrapDatabase($name); + + if ($this->eventManager->hasListeners(Events::postSelectDatabase)) { + $this->eventManager->dispatchEvent(Events::postSelectDatabase, new EventArgs($this, $database)); + } + + return $database; + } + + /** + * Method which creates a Doctrine\MongoDB\Database instance. + * + * @param string $name + * @return Database $database + */ + protected function wrapDatabase($name) + { + $numRetries = $this->config->getRetryQuery(); + if (null !== $this->config->getLoggerCallable()) { + return new LoggableDatabase( + $this, $name, $this->eventManager, $this->cmd, $numRetries, $this->config->getLoggerCallable() + ); + } + return new Database( + $this, $name, $this->eventManager, $this->cmd, $numRetries + ); + } + + protected function retry(\Closure $retry) + { + if (!$numRetries = $this->config->getRetryConnect()) { + return $retry(); + } + + $firstException = null; + for ($i = 0; $i <= $numRetries; $i++) { + try { + return $retry(); + } catch (\MongoException $e) { + if (!$firstException) { + $firstException = $e; + } + if ($i === $numRetries) { + throw $firstException; + } + } + } + + throw $e; + } + + public function __toString() + { + $this->initialize(); + return $this->mongo->__toString(); + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php new file mode 100644 index 00000000..6d473a96 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php @@ -0,0 +1,422 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\MongoDB\Util\ReadPreference; + +/** + * Wrapper for the PHP MongoCursor class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + */ +class Cursor implements Iterator +{ + /** The Doctrine Connection */ + protected $connection; + + /** The Doctrine Collection */ + protected $collection; + + /** The PHP MongoCursor being wrapped */ + protected $mongoCursor; + + /** Number of times to try operations on the cursor */ + protected $numRetries; + protected $query = array(); + protected $fields = array(); + protected $hints = array(); + protected $immortal; + protected $options = array(); + protected $batchSize; + protected $limit; + protected $skip; + protected $slaveOkay; + protected $snapshot; + protected $sorts = array(); + protected $tailable; + protected $timeout; + + /** + * Create a new MongoCursor which wraps around a given PHP MongoCursor. + * + * @param Connection $connection The Doctrine Connection instance. + * @param Collection $collection The Doctrine Collection that created this cursor. + * @param MongoCursor $mongoCursor The cursor being wrapped. + * @param array $query Query object for this cursor. + * @param array $fields Fields to select for this cursor. + * @param boolean|integer $numRetries Number of times to retry queries. + */ + public function __construct(Connection $connection, Collection $collection, \MongoCursor $mongoCursor, array $query = array(), array $fields = array(), $numRetries = 0) + { + $this->connection = $connection; + $this->collection = $collection; + $this->mongoCursor = $mongoCursor; + $this->query = $query; + $this->fields = $fields; + $this->numRetries = (integer) $numRetries; + } + + public function getConnection() + { + return $this->connection; + } + + public function getCollection() + { + return $this->collection; + } + + public function getQuery() + { + return $this->query; + } + + public function getFields() + { + return $this->fields; + } + + public function recreate() + { + $this->mongoCursor = $this->collection->getMongoCollection()->find($this->query, $this->fields); + foreach ($this->hints as $hint) { + $this->mongoCursor->hint($hint); + } + if ($this->immortal !== null) { + $this->mongoCursor->immortal($this->immortal); + } + foreach ($this->options as $key => $value) { + $this->mongoCursor->addOption($key, $value); + } + if ($this->batchSize !== null) { + $this->mongoCursor->batchSize($this->batchSize); + } + if ($this->limit !== null) { + $this->mongoCursor->limit($this->limit); + } + if ($this->skip !== null) { + $this->mongoCursor->skip($this->skip); + } + if ($this->slaveOkay !== null) { + $this->setMongoCursorSlaveOkay($this->slaveOkay); + } + if ($this->snapshot) { + $this->mongoCursor->snapshot(); + } + foreach ($this->sorts as $sort) { + $this->mongoCursor->sort($sort); + } + if ($this->tailable !== null) { + $this->mongoCursor->tailable($this->tailable); + } + if ($this->timeout !== null) { + $this->mongoCursor->timeout($this->timeout); + } + } + + /** + * Returns the MongoCursor instance being wrapped. + * + * @return MongoCursor $mongoCursor The MongoCursor instance being wrapped. + */ + public function getMongoCursor() + { + return $this->mongoCursor; + } + + public function current() + { + $current = $this->mongoCursor->current(); + if ($current instanceof \MongoGridFSFile) { + $document = $current->file; + $document['file'] = new GridFSFile($current); + $current = $document; + } + return $current; + } + + public function key() + { + return $this->mongoCursor->key(); + } + + public function dead() + { + return $this->mongoCursor->dead(); + } + + public function explain() + { + $cursor = $this; + return $this->retry(function() use ($cursor) { + return $cursor->getMongoCursor()->explain(); + }, true); + } + + public function fields(array $f) + { + $this->fields = $f; + $this->mongoCursor->fields($f); + return $this; + } + + public function getNext() + { + $cursor = $this; + $next = $this->retry(function() use ($cursor) { + return $cursor->getMongoCursor()->getNext(); + }, false); + if ($next instanceof \MongoGridFSFile) { + $document = $next->file; + $document['file'] = new GridFSFile($next); + $next = $document; + } + return $next; + } + + public function hasNext() + { + $cursor = $this; + return $this->retry(function() use ($cursor) { + return $cursor->getMongoCursor()->hasNext(); + }, false); + } + + public function hint(array $keyPattern) + { + $this->hints[] = $keyPattern; + $this->mongoCursor->hint($keyPattern); + return $this; + } + + public function immortal($liveForever = true) + { + $liveForever = (boolean) $liveForever; + $this->immortal = $liveForever; + $this->mongoCursor->immortal($liveForever); + return $this; + } + + public function info() + { + return $this->mongoCursor->info(); + } + + public function rewind() + { + $cursor = $this; + return $this->retry(function() use ($cursor) { + return $cursor->getMongoCursor()->rewind(); + }, false); + } + + public function next() + { + $cursor = $this; + return $this->retry(function() use ($cursor) { + return $cursor->getMongoCursor()->next(); + }, false); + } + + public function reset() + { + return $this->mongoCursor->reset(); + } + + public function count($foundOnly = false) + { + $cursor = $this; + return $this->retry(function() use ($cursor, $foundOnly) { + return $cursor->getMongoCursor()->count($foundOnly); + }, true); + } + + public function addOption($key, $value) + { + $this->options[$key] = $value; + $this->mongoCursor->addOption($key, $value); + return $this; + } + + public function batchSize($num) + { + $limit = (integer) $num; + $this->batchSize = $num; + $this->mongoCursor->batchSize($num); + return $this; + } + + public function limit($num) + { + $limit = (integer) $num; + $this->limit = $num; + $this->mongoCursor->limit($num); + return $this; + } + + public function skip($num) + { + $num = (integer) $num; + $this->skip = $num; + $this->mongoCursor->skip($num); + return $this; + } + + public function slaveOkay($ok = true) + { + $ok = (boolean) $ok; + $this->slaveOkay = $ok; + $this->setMongoCursorSlaveOkay($ok); + return $this; + } + + /** + * Set whether secondary read queries are allowed for this cursor. + * + * This method wraps setSlaveOkay() for driver versions before 1.3.0. For + * newer drivers, this method either wraps setReadPreference() method and + * specifies SECONDARY_PREFERRED or does nothing, depending on whether + * setReadPreference() exists. + * + * @param boolean $ok + */ + public function setMongoCursorSlaveOkay($ok) + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->mongoCursor->slaveOkay($ok); + return; + } + + /* MongoCursor::setReadPreference() may not exist until 1.4.0. Although + * we could throw an exception here, it's more user-friendly to NOP. + */ + if (!method_exists($this->mongoCursor, 'setReadPreference')) { + return; + } + + if ($ok) { + // Preserve existing tags for non-primary read preferences + $readPref = $this->mongoCursor->getReadPreference(); + $tags = !empty($readPref['tagsets']) ? ReadPreference::convertTagSets($readPref['tagsets']) : array(); + $this->mongoCursor->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags); + } else { + $this->mongoCursor->setReadPreference(\MongoClient::RP_PRIMARY); + } + } + + public function snapshot() + { + $this->snapshot = true; + $this->mongoCursor->snapshot(); + return $this; + } + + public function sort($fields) + { + foreach ($fields as $fieldName => $order) { + if (is_string($order)) { + $order = strtolower($order) === 'asc' ? 1 : -1; + } + $fields[$fieldName] = (integer) $order; + } + $this->sorts[] = $fields; + $this->mongoCursor->sort($fields); + return $this; + } + + public function tailable($tail = true) + { + $tail = (boolean) $tail; + $this->tailable = $tail; + $this->mongoCursor->tailable($tail); + return $this; + } + + public function timeout($ms) + { + $this->timeout = (integer) $ms; + $this->mongoCursor->timeout($ms); + return $this; + } + + public function valid() + { + return $this->mongoCursor->valid(); + } + + public function toArray($useKeys = true) + { + $cursor = $this; + return $this->retry(function() use ($cursor, $useKeys) { + return iterator_to_array($cursor, $useKeys); + }, true); + } + + /** + * Get the first single result from the cursor. + * + * @return array $document The single document. + */ + public function getSingleResult() + { + $originalLimit = $this->limit; + $this->limit(1); + $result = current($this->toArray()) ?: null; + $this->reset(); + $this->limit($originalLimit); + return $result; + } + + protected function retry(\Closure $retry, $recreate = false) + { + if ($this->numRetries) { + $firstException = null; + for ($i = 0; $i <= $this->numRetries; $i++) { + $reconnect = false; + try { + return $retry(); + } catch (\MongoCursorTimeoutException $e) { + } catch (\MongoCursorException $e) { + } catch (\MongoConnectionException $e) { + $reconnect = true; + } + if (isset($e)) { + if (!$firstException) { + $firstException = $e; + } + if ($i === $this->numRetries) { + throw $firstException; + } + if ($recreate) { + if ($reconnect) { + $this->connection->initialize(true); + } + $this->recreate(); + } + } + } + } else { + return $retry(); + } + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Database.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Database.php new file mode 100644 index 00000000..b23e52b1 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Database.php @@ -0,0 +1,320 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\Common\EventManager; +use Doctrine\MongoDB\Event\EventArgs; +use Doctrine\MongoDB\Util\ReadPreference; + +/** + * Wrapper for the PHP MongoDB class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +class Database +{ + /** + * Doctrine mongodb connection instance. + * + * @var Doctrine\MongoDB\Connection + */ + protected $connection; + + /** + * The name of the database + * + * @var string $Name + */ + protected $name; + + /** + * The event manager that is the central point of the event system. + * + * @var Doctrine\Common\EventManager + */ + protected $eventManager; + + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * Number of times to retry queries. + * + * @var mixed + */ + protected $numRetries; + + /** + * Create a new MongoDB instance which wraps a PHP MongoDB instance. + * + * @param Connection $connection The Doctrine Connection instance. + * @param string $name The name of the database. + * @param EventManager $evm The EventManager instance. + * @param string $cmd The MongoDB cmd character. + * @param boolean|integer $numRetries Number of times to retry queries. + */ + public function __construct(Connection $connection, $name, EventManager $evm, $cmd, $numRetries = 0) + { + $this->connection = $connection; + $this->name = $name; + $this->eventManager = $evm; + $this->cmd = $cmd; + $this->numRetries = (integer) $numRetries; + } + + /** + * Gets the name of this database + * + * @return string $name + */ + public function getName() + { + return $this->name; + } + + /** + * Get the MongoDB instance being wrapped. + * + * @return MongoDB $mongoDB + */ + public function getMongoDB() + { + return $this->connection->getMongo()->selectDB($this->name); + } + + public function authenticate($username, $password) + { + return $this->getMongoDB()->authenticate($username, $password); + } + + public function command(array $data, array $options = array()) + { + return $this->getMongoDB()->command($data, $options); + } + + public function createCollection($name, $capped = false, $size = 0, $max = 0) + { + if ($this->eventManager->hasListeners(Events::preCreateCollection)) { + $this->eventManager->dispatchEvent(Events::preCreateCollection, new CreateCollectionEventArgs($this, $name, $capped, $size, $max)); + } + + $this->getMongoDB()->createCollection($name, $capped, $size, $max); + + $result = $this->selectCollection($name); + + if ($this->eventManager->hasListeners(Events::postCreateCollection)) { + $this->eventManager->dispatchEvent(Events::postCreateCollection, new EventArgs($this, $prefix)); + } + + return $result; + } + + public function createDBRef($collection, $a) + { + return $this->getMongoDB()->createDBRef($collection, $a); + } + + public function drop() + { + if ($this->eventManager->hasListeners(Events::preDropDatabase)) { + $this->eventManager->dispatchEvent(Events::preDropDatabase, new EventArgs($this)); + } + + $result = $this->getMongoDB()->drop(); + + if ($this->eventManager->hasListeners(Events::postDropDatabase)) { + $this->eventManager->dispatchEvent(Events::postDropDatabase, new EventArgs($this)); + } + + return $result; + } + + public function dropCollection($coll) + { + return $this->getMongoDB()->dropCollection($coll); + } + + public function execute($code, array $args = array()) + { + return $this->getMongoDB()->execute($code, $args); + } + + public function forceError() + { + return $this->getMongoDB()->forceError(); + } + + public function __get($name) + { + return $this->getMongoDB()->__get($name); + } + + public function getDBRef(array $ref) + { + return $this->getMongoDB()->getDBRef($ref); + } + + public function getGridFS($prefix = 'fs') + { + if ($this->eventManager->hasListeners(Events::preGetGridFS)) { + $this->eventManager->dispatchEvent(Events::preGetGridFS, new EventArgs($this, $prefix)); + } + + $gridFS = $this->doGetGridFs($prefix); + + if ($this->eventManager->hasListeners(Events::preGetGridFS)) { + $this->eventManager->dispatchEvent(Events::preGetGridFS, new EventArgs($this, $gridFS)); + } + + return $gridFS; + } + + protected function doGetGridFs($name) + { + return new GridFS( + $this->connection, $name, $this, $this->eventManager, $this->cmd + ); + } + + /** + * Set whether secondary read queries are allowed for this database. + * + * This method wraps setSlaveOkay() for driver versions before 1.3.0. For + * newer drivers, this method wraps setReadPreference() and specifies + * SECONDARY_PREFERRED. + */ + public function setSlaveOkay($ok = true) + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + return $this->getMongoDB()->setSlaveOkay($ok); + } + + $prevSlaveOkay = $this->getSlaveOkay(); + + if ($ok) { + // Preserve existing tags for non-primary read preferences + $readPref = $this->getMongoDB()->getReadPreference(); + $tags = !empty($readPref['tagsets']) ? ReadPreference::convertTagSets($readPref['tagsets']) : array(); + $this->getMongoDB()->setReadPreference(\MongoClient::RP_SECONDARY_PREFERRED, $tags); + } else { + $this->getMongoDB()->setReadPreference(\MongoClient::RP_PRIMARY); + } + + return $prevSlaveOkay; + } + + /** + * Get whether secondary read queries are allowed for this database. + * + * This method wraps getSlaveOkay() for driver versions before 1.3.0. For + * newer drivers, this method considers any read preference other than + * PRIMARY as a true "slaveOkay" value. + */ + public function getSlaveOkay() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + return $this->getMongoDB()->getSlaveOkay(); + } + + $readPref = $this->getMongoDB()->getReadPreference(); + + if (is_numeric($readPref['type'])) { + $readPref['type'] = ReadPreference::convertNumericType($readPref['type']); + } + + return \MongoClient::RP_PRIMARY !== $readPref['type']; + } + + public function getProfilingLevel() + { + return $this->getMongoDB()->getProfilingLevel(); + } + + public function lastError() + { + return $this->getMongoDB()->lastError(); + } + + public function listCollections() + { + return $this->getMongoDB()->listCollections(); + } + + public function prevError() + { + return $this->getMongoDB()->prevError(); + } + + public function repair($preserveClonedFiles = false, $backupOriginalFiles = false) + { + return $this->getMongoDB()->repair($preserveClonedFiles, $backupOriginalFiles); + } + + public function resetError() + { + return $this->getMongoDB()->resetError(); + } + + public function selectCollection($name) + { + if ($this->eventManager->hasListeners(Events::preSelectCollection)) { + $this->eventManager->dispatchEvent(Events::preSelectCollection, new EventArgs($this, $name)); + } + + $collection = $this->doSelectCollection($name); + + if ($this->eventManager->hasListeners(Events::postSelectCollection)) { + $this->eventManager->dispatchEvent(Events::postSelectCollection, new EventArgs($this, $collection)); + } + + return $collection; + } + + /** + * Method which creates a Doctrine\MongoDB\Collection instance. + * + * @param string $name + * @return Collection $coll + */ + protected function doSelectCollection($name) + { + return new Collection( + $this->connection, $name, $this, $this->eventManager, $this->cmd, $this->numRetries + ); + } + + public function setProfilingLevel($level) + { + return $this->getMongoDB()->setProfilingLevel($level); + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/EagerCursor.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/EagerCursor.php new file mode 100644 index 00000000..6f144619 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/EagerCursor.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * EagerCursor class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + */ +class EagerCursor implements Iterator +{ + /** @var object Doctrine\MongoDB\Cursor */ + protected $cursor; + + /** @var array Array of data from Cursor to iterate over */ + protected $data = array(); + + /** @var bool Whether or not the EagerCursor has been initialized */ + protected $initialized = false; + + public function __construct(Cursor $cursor) + { + $this->cursor = $cursor; + } + + public function getCursor() + { + return $this->cursor; + } + + public function isInitialized() + { + return $this->initialized; + } + + public function initialize() + { + if ($this->initialized === false) { + $this->data = $this->cursor->toArray(); + unset($this->cursor); + } + $this->initialized = true; + } + + public function rewind() + { + $this->initialize(); + reset($this->data); + } + + public function current() + { + $this->initialize(); + return current($this->data); + } + + public function key() + { + $this->initialize(); + return key($this->data); + } + + public function next() + { + $this->initialize(); + return next($this->data); + } + + public function valid() + { + $this->initialize(); + $key = key($this->data); + return ($key !== NULL && $key !== FALSE); + } + + public function count() + { + $this->initialize(); + return count($this->data); + } + + public function toArray() + { + $this->initialize(); + return $this->data; + } + + public function getSingleResult() + { + $this->initialize(); + return $this->current(); + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/CreateCollectionEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/CreateCollectionEventArgs.php new file mode 100644 index 00000000..c5468721 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/CreateCollectionEventArgs.php @@ -0,0 +1,73 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Create collection event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class CreateCollectionEventArgs extends BaseEventArgs +{ + private $invoker; + private $name; + private $capped; + private $size; + private $max; + + public function __construct($invoker, &$name, &$capped, &$size, &$max) + { + $this->invoker = $invoker; + $this->name = $name; + $this->capped = $capped; + $this->size = $size; + $this->max = $max; + } + + public function getInvoker() + { + return $this->invoker; + } + + public function getName() + { + return $this->name; + } + + public function getCapped() + { + return $this->capped; + } + + public function getSize() + { + return $this->size; + } + + public function getMax() + { + return $this->max; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/DistinctEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/DistinctEventArgs.php new file mode 100644 index 00000000..ccc9ab02 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/DistinctEventArgs.php @@ -0,0 +1,54 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Distinct event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class DistinctEventArgs extends BaseEventArgs +{ + private $invoker; + private $field; + private $query = array(); + + public function __construct($invoker, &$field, array &$query) + { + $this->invoker = $invoker; + $this->field = $field; + $this->query = $query; + } + + public function getField() + { + return $this->field; + } + + public function getQuery() + { + return $this->query; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/EventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/EventArgs.php new file mode 100644 index 00000000..068318fe --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/EventArgs.php @@ -0,0 +1,52 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class EventArgs extends BaseEventArgs +{ + private $invoker; + private $data; + + public function __construct($invoker, &$data = null) + { + $this->invoker = $invoker; + $this->data = $data; + } + + public function getInvoker() + { + return $this->invoker; + } + + public function getData() + { + return $this->data; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/GroupEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/GroupEventArgs.php new file mode 100644 index 00000000..3d3f27f6 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/GroupEventArgs.php @@ -0,0 +1,61 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Group event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class GroupEventArgs extends BaseEventArgs +{ + private $invoker; + private $keys; + private $initial = array(); + private $reduce; + + public function __construct($invoker, &$keys, array &$initial, &$reduce) + { + $this->invoker = $invoker; + $this->keys = $keys; + $this->initial = $initial; + $this->reduce = $reduce; + } + + public function getKeys() + { + return $this->keys; + } + + public function getInitial() + { + return $this->initial; + } + + public function getReduce() + { + return $this->reduce; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/MapReduceEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/MapReduceEventArgs.php new file mode 100644 index 00000000..5e7078fc --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/MapReduceEventArgs.php @@ -0,0 +1,68 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Map reduce event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class MapReduceEventArgs extends BaseEventArgs +{ + private $invoker; + private $map; + private $reduce; + private $query; + private $out; + + public function __construct($invoker, &$map, &$reduce, array &$out, array &$query) + { + $this->invoker = $invoker; + $this->map = $map; + $this->reduce = $reduce; + $this->out = $out; + $this->query = $query; + } + + public function getKeys() + { + return $this->keys; + } + + public function getReduce() + { + return $this->reduce; + } + + public function getQuery() + { + return $this->query; + } + + public function getOut() + { + return $this->out; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/NearEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/NearEventArgs.php new file mode 100644 index 00000000..67511bf5 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/NearEventArgs.php @@ -0,0 +1,59 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Near event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class NearEventArgs extends BaseEventArgs +{ + private $invoker; + private $query = array(); + private $near = array(); + + public function __construct($invoker, array &$query, array &$near) + { + $this->invoker = $invoker; + $this->query = $query; + $this->near = $near; + } + + public function getInvoker() + { + return $this->invoker; + } + + public function getQuery() + { + return $this->query; + } + + public function getNear() + { + return $this->near; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/UpdateEventArgs.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/UpdateEventArgs.php new file mode 100644 index 00000000..1248ef16 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Event/UpdateEventArgs.php @@ -0,0 +1,59 @@ +. +*/ + +namespace Doctrine\MongoDB\Event; + +use Doctrine\Common\EventArgs as BaseEventArgs; + +/** + * Update event args. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class UpdateEventArgs extends BaseEventArgs +{ + private $invoker; + private $query = array(); + private $newObj = array(); + + public function __construct($invoker, &$query, &$newObj) + { + $this->invoker = $invoker; + $this->query = $query; + $this->newObj = $newObj; + } + + public function getInvoker() + { + return $this->invoker; + } + + public function getQuery() + { + return $this->query; + } + + public function getNewObj() + { + return $this->newObj; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Events.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Events.php new file mode 100644 index 00000000..1907f1f7 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Events.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Container for all Doctrine\MongoDB events. + * + * This class cannot be instantiated. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +final class Events +{ + private function __construct() {} + + const preBatchInsert = 'collectionPreBatchInsert'; + const postBatchInsert = 'collectionPostBatchInsert'; + + const preSave = 'collectionPreSave'; + const postSave = 'collectionPostSave'; + + const preInsert = 'collectionPreInsert'; + const postInsert = 'collectionPostInsert'; + + const preUpdate = 'collectionPreUpdate'; + const postUpdate = 'collectionPostUpdate'; + + const preRemove = 'collectionPreRemove'; + const postRemove = 'collectionPostRemove'; + + const preFind = 'collectionPreFind'; + const postFind = 'collectionPostFind'; + + const preFindOne = 'collectionPreFindOne'; + const postFindOne = 'collectionPostFindOne'; + + const preFindAndRemove = 'collectionPreFindAndRemove'; + const postFindAndRemove = 'collectionPostFindAndRemove'; + + const preFindAndUpdate = 'collectionPreFindAndUpdate'; + const postFindAndUpdate = 'collectionPostFindAndUpdate'; + + const preGroup = 'collectionPreGroup'; + const postGroup = 'collectionPostGroup'; + + const preGetDBRef = 'collectionPreGetDBRef'; + const postGetDBRef = 'collectionPostGetDBRef'; + + const preCreateDBRef = 'collectionPreCreateDBRef'; + const postCreateDBRef = 'collectionPostCreateDBRef'; + + const preDistinct = 'collectionPreDistinct'; + const postDistinct = 'collectionPostDistinct'; + + const preMapReduce = 'preMapReduce'; + const postMapReduce = 'postMapReduce'; + + const preNear = 'collectionPreNear'; + const postNear = 'collectionPostNear'; + + const preCreateCollection = 'preCreateCollection'; + const postCreateCollection = 'postCreateCollection'; + + const preSelectDatabase = 'preSelectDatabase'; + const postSelectDatabase = 'postSelectDatabase'; + + const preDropDatabase = 'preDropDatabase'; + const postDropDatabase = 'postDropDatabase'; + + const preSelectCollection = 'preSelectCollection'; + const postSelectCollection = 'postSelectCollection'; + + const preDropCollection = 'preDropCollection'; + const postDropCollection = 'postDropCollection'; + + const preGetGridFS = 'preGetGridFS'; + const postGetGridFS = 'postGetGridFS'; + + const preConnect = 'preConnect'; + const postConnect = 'postConnect'; +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFS.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFS.php new file mode 100644 index 00000000..7a652367 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFS.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Wrapper for the PHP MongoGridFS class + * + * This class does not proxy all of the MongoGridFS methods; however, the + * MongoGridFS object is accessible if those methods are required. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + */ +class GridFS extends Collection +{ + /** @override */ + public function getMongoCollection() + { + return $this->database->getMongoDB()->getGridFS($this->name); + } + + /** @override */ + protected function doFindOne(array $query = array(), array $fields = array()) + { + $collection = $this; + $file = $this->retry(function() use ($collection, $query, $fields) { + return $collection->getMongoCollection()->findOne($query, $fields); + }); + if ($file) { + $document = $file->file; + $document['file'] = new GridFSFile($file); + $file = $document; + } + return $file; + } + + /** @override */ + protected function doUpdate($query, array $newObj, array $options = array()) + { + if (is_scalar($query)) { + $query = array('_id' => $query); + } + + $file = isset($newObj[$this->cmd.'set']['file']) ? $newObj[$this->cmd.'set']['file'] : null; + unset($newObj[$this->cmd.'set']['file']); + + if ($file === null) { + $file = isset($newObj['file']) ? $newObj['file'] : null; + unset($newObj['file']); + } + + /* Before we inspect $newObj, remove an empty $set operator we may have + * left behind due to extracting the file field above. + */ + if (empty($newObj[$this->cmd.'set'])) { + unset($newObj[$this->cmd.'set']); + } + + /* Determine if $newObj includes atomic modifiers, which will tell us if + * we can get by with a storeFile() in some situations or if a two-step + * storeFile() and update() is necessary. + */ + $newObjHasModifiers = false; + + foreach (array_keys($newObj) as $key) { + if ($this->cmd === $key[0]) { + $newObjHasModifiers = true; + } + } + + // Is there a file in need of persisting? + if (isset($file) && $file->isDirty()) { + /* It is impossible to overwrite a file's chunks in GridFS so we + * must remove it and re-persist a new file with the same data. + * + * First, use findAndRemove() to remove the file metadata and chunks + * prior to storing the file again below. Exclude metadata fields + * from the result, since those will be reset later. + */ + $document = $this->findAndRemove($query, array('fields' => array( + 'filename' => 0, + 'length' => 0, + 'chunkSize' => 0, + 'uploadDate' => 0, + 'md5' => 0, + 'file' => 0, + ))); + + /* If findAndRemove() returned nothing (no match or removal), create + * a new document with the query's "_id" if available. + */ + if (!isset($document)) { + /* If $newObj had modifiers, we'll need to do an update later, + * so default to an empty array for now. Otherwise, we can do + * without that update and store $newObj now. + */ + $document = $newObjHasModifiers ? array() : $newObj; + + /* If the document has no "_id" but there was one in the query + * or $newObj, we can use that instead of having storeFile() + * generate one. + */ + if (!isset($document['_id']) && isset($query['_id'])) { + $document['_id'] = $query['_id']; + } + + if (!isset($document['_id']) && isset($newObj['_id'])) { + $document['_id'] = $newObj['_id']; + } + } + + // Document will definitely have an "_id" after storing the file. + $this->storeFile($file, $document); + + if (!$newObjHasModifiers) { + /* TODO: MongoCollection::update() would return a boolean if + * $newObj was not empty, or an array describing the update + * operation. Improvise, since we only stored the file and that + * returns the "_id" field. + */ + return true; + } + } + + // Now send the original update bringing the file up to date + return $this->getMongoCollection()->update($query, $newObj, $options); + } + + /** @override */ + protected function doBatchInsert(array &$a, array $options = array()) + { + foreach ($a as $key => &$array) { + $this->doInsert($array, $options); + } + } + + /** @override */ + protected function doInsert(array &$a, array $options = array()) + { + // If there is no file, perform a basic insertion + if (!isset($a['file'])) { + parent::doInsert($a, $options); + return; + } + + /* If the file is dirty (i.e. it must be persisted), delegate to the + * storeFile() method. Otherwise, perform a basic insertion. + */ + $file = $a['file']; // instanceof GridFSFile + unset($a['file']); + + if ($file->isDirty()) { + $this->storeFile($file, $a, $options); + } else { + parent::doInsert($a, $options); + } + + $a['file'] = $file; + return $a; + } + + /** @override */ + protected function doSave(array &$a, array $options = array()) + { + if (isset($a['_id'])) { + return $this->doUpdate(array('_id' => $a['_id']), $a, $options); + } else { + return $this->doInsert($a, $options); + } + } + + /** + * Store a file on the mongodb grid file system. + * + * @param string|GridFSFile $file String path to a file or a GridFSFile object. + * @param object $document + * @param array $options + * @return GridFSFile $file + */ + public function storeFile($file, array &$document, array $options = array()) + { + if (!$file instanceof GridFSFile) { + $file = new GridFSFile($file); + } + + if ($file->hasUnpersistedFile()) { + $id = $this->getMongoCollection()->storeFile($file->getFilename(), $document, $options); + } else { + $id = $this->getMongoCollection()->storeBytes($file->getBytes(), $document, $options); + } + + $document = array_merge(array('_id' => $id), $document); + $gridFsFile = $this->getMongoCollection()->get($id); + + // TODO: Consider throwing exception if file cannot be fetched + $file->setMongoGridFSFile($this->getMongoCollection()->get($id)); + + return $file; + } + + protected function doFindAndRemove(array $query, array $options = array()) + { + $document = parent::doFindAndRemove($query, $options); + + if (isset($document)) { + // Remove the file data from the chunks collection + $this->getMongoCollection()->chunks->remove(array('files_id' => $document['_id']), $options); + } + + return $document; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFSFile.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFSFile.php new file mode 100644 index 00000000..239c3bd9 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/GridFSFile.php @@ -0,0 +1,228 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * File is a wrapper around the native PHP MongoGridFSFile class and allows you + * to use an instance of this class to persist new files as well as represent + * existing, already persisted files using the MongoGridFS. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +class GridFSFile +{ + /** + * Stores \MongoGridFSFile instance. + * + * @var \MongoGridFSFile + */ + private $mongoGridFSFile; + + /** + * Path to a file that is/was pending persistence. + * + * @var string + */ + private $filename; + + /** + * Bytes that are/were pending persistence. + * + * @var string + */ + private $bytes; + + /** + * Whether or not the file is dirty and needs to be persisted. + * + * @var string + */ + private $isDirty = false; + + /** + * Constructs a new dirty file that needs persisting or wraps an existing PHP \MongoGridFSFile + * instance and does not need persistence unless changed and becomes dirty. + * + * @param string|\MongoGridFSFile $file + */ + public function __construct($file = null) + { + if ($file instanceof \MongoGridFSFile) { + $this->mongoGridFSFile = $file; + $this->isDirty = false; + } elseif (is_string($file)) { + $this->filename = $file; + $this->isDirty = true; + } + } + + /** + * Sets the persistent MongoGridFSFile instance + * + * @param \MongoGridFSFile $mongoGridFSFile + */ + public function setMongoGridFSFile(\MongoGridFSFile $mongoGridFSFile) + { + $this->mongoGridFSFile = $mongoGridFSFile; + $this->isDirty = false; + } + + /** + * Gets the persistent MongoGridFSFile instance + * + * @return \MongoGridFSFile + */ + public function getMongoGridFSFile() + { + return $this->mongoGridFSFile; + } + + /** + * Set a new filename to be persisted and marks the file as dirty. + * + * @param string $filename + */ + public function setFilename($filename) + { + $this->filename = $filename; + $this->isDirty = true; + } + + /** + * Gets the filename for this file. + * + * @return string $filename + */ + public function getFilename() + { + if ($this->isDirty && $this->filename) { + return $this->filename; + } elseif ($this->mongoGridFSFile instanceof \MongoGridFSFile && $filename = $this->mongoGridFSFile->getFilename()) { + return $filename; + } + return $this->filename; + } + + /** + * Sets new bytes to be persisted and marks the file as dirty. + * + * @param string $bytes + */ + public function setBytes($bytes) + { + $this->bytes = $bytes; + $this->isDirty = true; + } + + /** + * Gets the bytes for this file. + * + * @return string $bytes + */ + public function getBytes() + { + if ($this->isDirty && $this->bytes) { + return $this->bytes; + } + if ($this->isDirty && $this->filename) { + return file_get_contents($this->filename); + } + if ($this->mongoGridFSFile instanceof \MongoGridFSFile) { + return $this->mongoGridFSFile->getBytes(); + } + return null; + } + + /** + * Gets the size of this file. + * + * @return integer $size + */ + public function getSize() + { + if ($this->isDirty && $this->bytes) { + return strlen($this->bytes); + } + if ($this->isDirty && $this->filename) { + return filesize($this->filename); + } + if ($this->mongoGridFSFile instanceof \MongoGridFSFile) { + return $this->mongoGridFSFile->getSize(); + } + return 0; + } + + /** + * Writes this file to the given filename path. + * + * @param string $filename + * @return boolean TRUE if successful, and FALSE otherwise. + */ + public function write($filename) + { + if ($this->isDirty && $this->bytes) { + return file_put_contents($filename, $this->bytes); + } + if ($this->isDirty && $this->filename) { + return copy($this->filename, $filename); + } + if ($this->mongoGridFSFile instanceof \MongoGridFSFile) { + return $this->mongoGridFSFile->write($filename); + } + throw new \BadMethodCallException('Nothing to write(). File is not persisted yet and is not dirty.'); + } + + /** + * Check if the file is dirty or set isDirty by passing a boolean argument. + * + * @param boolean $isDirty + * @param boolean + */ + public function isDirty($isDirty = null) + { + if ($isDirty !== null) { + $this->isDirty = (boolean) $isDirty; + } + return $this->isDirty; + } + + /** + * Checks whether the file has some unpersisted bytes. + * + * @return boolean + */ + public function hasUnpersistedBytes() + { + return ($this->isDirty && $this->bytes); + } + + /** + * Checks whether the file has a unpersisted file. + * + * @return boolean + */ + public function hasUnpersistedFile() + { + return ($this->isDirty && $this->filename); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Iterator.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Iterator.php new file mode 100644 index 00000000..ec30262d --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Iterator.php @@ -0,0 +1,34 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Iterator interface. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + */ +interface Iterator extends \Iterator, \Countable +{ + function toArray(); + function getSingleResult(); +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/IteratorAggregate.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/IteratorAggregate.php new file mode 100644 index 00000000..4cbff9ab --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/IteratorAggregate.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * IteratorAggregate interface. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +interface IteratorAggregate extends \IteratorAggregate, \Countable +{ + function toArray(); + function getSingleResult(); +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Loggable.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Loggable.php new file mode 100644 index 00000000..11fe74d4 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Loggable.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Loggable interface + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Bulat Shakirzyanov + */ +interface Loggable +{ + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + function log(array $log); +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCollection.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCollection.php new file mode 100644 index 00000000..1241b742 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCollection.php @@ -0,0 +1,259 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\Common\EventManager, + Doctrine\ODM\Event\EventArgs; + +/** + * Wrapper for the PHP MongoCollection class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ + +class LoggableCollection extends Collection implements Loggable +{ + /** + * A callable for logging statements. + * + * @var mixed + */ + protected $loggerCallable; + + /** + * Create a new MongoCollection instance that wraps a PHP MongoCollection instance + * for a given ClassMetadata instance. + * + * @param Connection $connection The Doctrine Connection instance. + * @param string $name The name of the collection. + * @param Database $database The Database instance. + * @param EventManager $evm The EventManager instance. + * @param string $cmd Mongo cmd character. + * @param Closure $loggerCallable The logger callable. + * @param boolean|integer $numRetries Number of times to retry queries. + */ + public function __construct(Connection $connection, $name, Database $database, EventManager $evm, $cmd, $loggerCallable, $numRetries = 0) + { + if ( ! is_callable($loggerCallable)) { + throw new \InvalidArgumentException('$loggerCallable must be a valid callback'); + } + $this->loggerCallable = $loggerCallable; + parent::__construct($connection, $name, $database, $evm, $cmd, $numRetries); + } + + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + public function log(array $log) + { + $log['db'] = $this->database->getName(); + $log['collection'] = $this->getName(); + call_user_func_array($this->loggerCallable, array($log)); + } + + /** @override */ + public function batchInsert(array &$a, array $options = array()) + { + $this->log(array( + 'batchInsert' => true, + 'num' => count($a), + 'data' => $a, + 'options' => $options + )); + + return parent::batchInsert($a, $options); + } + + /** @override */ + public function update($query, array $newObj, array $options = array()) + { + $this->log(array( + 'update' => true, + 'query' => $query, + 'newObj' => $newObj, + 'options' => $options + )); + + return parent::update($query, $newObj, $options); + } + + /** @override */ + public function find(array $query = array(), array $fields = array()) + { + $this->log(array( + 'find' => true, + 'query' => $query, + 'fields' => $fields + )); + + return parent::find($query, $fields); + } + + /** @override */ + public function findOne(array $query = array(), array $fields = array()) + { + $this->log(array( + 'findOne' => true, + 'query' => $query, + 'fields' => $fields + )); + + return parent::findOne($query, $fields); + } + + public function count(array $query = array(), $limit = 0, $skip = 0) + { + $this->log(array( + 'count' => true, + 'query' => $query, + 'limit' => $limit, + 'skip' => $skip + )); + + return parent::count($query, $limit, $skip); + } + + public function createDBRef(array $a) + { + $this->log(array( + 'createDBRef' => true, + 'reference' => $a + )); + + return parent::createDBRef($a); + } + + public function deleteIndex($keys) + { + $this->log(array( + 'deleteIndex' => true, + 'keys' => $keys + )); + + return parent::deleteIndex($keys); + } + + public function deleteIndexes() + { + $this->log(array( + 'deleteIndexes' => true + )); + + return parent::deleteIndexes(); + } + + public function drop() + { + $this->log(array( + 'drop' => true + )); + + return parent::drop(); + } + + public function ensureIndex(array $keys, array $options = array()) + { + $this->log(array( + 'ensureIndex' => true, + 'keys' => $keys, + 'options' => $options + )); + + return parent::ensureIndex($keys, $options); + } + + public function getDBRef(array $reference) + { + $this->log(array( + 'getDBRef' => true, + 'reference' => $reference + )); + + return parent::getDBRef($reference); + } + + public function group($keys, array $initial, $reduce, array $options = array()) + { + $this->log(array( + 'group' => true, + 'keys' => $keys, + 'initial' => $initial, + 'reduce' => $reduce, + 'options' => $options + )); + + return parent::group($keys, $initial, $reduce, $options); + } + + public function insert(array &$a, array $options = array()) + { + $this->log(array( + 'insert' => true, + 'document' => $a, + 'options' => $options + )); + + return parent::insert($a, $options); + } + + public function remove(array $query, array $options = array()) + { + $this->log(array( + 'remove' => true, + 'query' => $query, + 'options' => $options + )); + + return parent::remove($query, $options); + } + + public function save(array &$a, array $options = array()) + { + $this->log(array( + 'save' => true, + 'document' => $a, + 'options' => $options + )); + + return parent::save($a, $options); + } + + public function validate($scanData = false) + { + $this->log(array( + 'validate' => true, + 'scanData' => $scanData + )); + + return parent::validate($scanData); + } + + /** @override */ + protected function wrapCursor(\MongoCursor $cursor, $query, $fields) + { + return new LoggableCursor($this->connection, $this, $cursor, $this->loggerCallable, $query, $fields, $this->numRetries); + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCursor.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCursor.php new file mode 100644 index 00000000..1d96269a --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableCursor.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\MongoDB; + +/** + * Wrapper for the PHP MongoCursor class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + */ +class LoggableCursor extends Cursor implements Loggable +{ + /** + * A callable for logging statements. + * + * @var mixed + */ + protected $loggerCallable; + + /** + * The query array that was used when creating this cursor. + * + * @var array + */ + protected $query = array(); + + /** + * The array of fields that were selected when creating this cursor. + * + * @var array + */ + protected $fields = array(); + + /** + * Create a new MongoCursor which wraps around a given PHP MongoCursor. + * + * @param Connection $connection The Doctrine Connection instance. + * @param Collection $collection The Doctrine Collection that created this cursor. + * @param MongoCursor $mongoCursor The cursor being wrapped. + * @param mixed $loggerCallable Logger callable. + * @param array $query The query array that was used to create this cursor. + * @param array $query The fields selected on this cursor. + * @param boolean|integer $numRetries Number of times to retry queries. + */ + public function __construct(Connection $connection, Collection $collection, \MongoCursor $mongoCursor, $loggerCallable, array $query, array $fields, $numRetries = 0) + { + if ( ! is_callable($loggerCallable)) { + throw new \InvalidArgumentException('$loggerCallable must be a valid callback'); + } + parent::__construct($connection, $collection, $mongoCursor, $query, $fields, $numRetries); + $this->loggerCallable = $loggerCallable; + $this->query = $query; + $this->fields = $fields; + } + + /** + * Gets the logger callable. + * + * @return mixed The logger callable + */ + public function getLoggerCallable() + { + return $this->loggerCallable; + } + + /** + * Gets the query array that was used when creating this cursor. + * + * @return array $query + */ + public function getQuery() + { + return $this->query; + } + + /** + * Gets the array of fields that were selected when creating this cursor. + * + * @return array $fields + */ + public function getFields() + { + return $this->fields; + } + + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + public function log(array $log) + { + $log['query'] = $this->query; + $log['fields'] = $this->fields; + call_user_func_array($this->loggerCallable, array($log)); + } + + public function sort($fields) + { + $this->log(array( + 'sort' => true, + 'sortFields' => $fields + )); + + return parent::sort($fields); + } + + public function skip($num) + { + $this->log(array( + 'skip' => true, + 'skipNum' => $num, + )); + + return parent::skip($num); + } + + public function limit($num) + { + $this->log(array( + 'limit' => true, + 'limitNum' => $num, + )); + + return parent::limit($num); + } + + public function hint(array $keyPattern) + { + $this->log(array( + 'hint' => true, + 'keyPattern' => $keyPattern, + )); + + return parent::hint($keyPattern); + } + + public function snapshot() + { + $this->log(array( + 'snapshot' => true, + )); + + return parent::snapshot(); + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableDatabase.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableDatabase.php new file mode 100644 index 00000000..b7fa5cfc --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/LoggableDatabase.php @@ -0,0 +1,159 @@ +. + */ + +namespace Doctrine\MongoDB; + +use Doctrine\Common\EventManager; + +/** + * Wrapper for the PHP MongoDB class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +class LoggableDatabase extends Database implements Loggable +{ + /** + * A callable for logging statements. + * + * @var mixed + */ + protected $loggerCallable; + + /** + * Create a new MongoDB instance which wraps a PHP MongoDB instance. + * + * @param Connection $connection The Doctrine Connection instance. + * @param string $name The name of the database. + * @param EventManager $evm The EventManager instance. + * @param string $cmd The MongoDB cmd character. + * @param mixed $numRetries Number of times to retry queries. + * @param Closure $loggerCallable Logger callback function. + */ + public function __construct(Connection $connection, $name, EventManager $evm, $cmd, $numRetries, $loggerCallable) + { + if ( ! is_callable($loggerCallable)) { + throw new \InvalidArgumentException('$loggerCallable must be a valid callback'); + } + parent::__construct($connection, $name, $evm, $cmd, $numRetries); + $this->loggerCallable = $loggerCallable; + } + + /** + * Log something using the configured logger callable. + * + * @param array $log The array of data to log. + */ + public function log(array $log) + { + $log['db'] = $this->getName(); + call_user_func_array($this->loggerCallable, array($log)); + } + + public function authenticate($username, $password) + { + $this->log(array( + 'authenticate' => true, + 'username' => $username, + 'password' => $password + )); + + return parent::authenticate($username, $password); + } + + public function command(array $data, array $options = array()) + { + $this->log(array( + 'command' => true, + 'data' => $data, + 'options' => $options + )); + + return parent::command($data, $options); + } + + public function createCollection($name, $capped = false, $size = 0, $max = 0) + { + $this->log(array( + 'createCollection' => true, + 'capped' => $capped, + 'size' => $size, + 'max' => $max + )); + + return parent::createCollection($name, $capped, $size, $max); + } + + public function createDBRef($collection, $a) + { + $this->log(array( + 'createDBRef' => true, + 'collection' => $collection, + 'reference' => $a + )); + + return parent::createDBRef($collection, $a); + } + + public function drop() + { + $this->log(array( + 'dropDatabase' => true + )); + + return parent::drop(); + } + + public function execute($code, array $args = array()) + { + $this->log(array( + 'execute' => true, + 'code' => $code, + 'args' => $args + )); + + return parent::execute($code, $args); + } + + public function getDBRef(array $ref) + { + $this->log(array( + 'getDBRef' => true, + 'reference' => $ref + )); + + return parent::getDBRef($ref); + } + + /** + * Method which creates a Doctrine\MongoDB\Collection instance. + * + * @param string $name + * @return Collection $coll + */ + protected function doSelectCollection($name) + { + return new LoggableCollection( + $this->connection, $name, $this, $this->eventManager, $this->cmd, $this->loggerCallable, $this->numRetries + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Builder.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Builder.php new file mode 100644 index 00000000..a3f88be9 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Builder.php @@ -0,0 +1,1118 @@ +. + */ + +namespace Doctrine\MongoDB\Query; + +use Doctrine\MongoDB\Query\Expr; +use Doctrine\MongoDB\Database; +use Doctrine\MongoDB\Collection; + +/** + * Fluent query builder interface. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @since 1.0 + * @author Jonathan H. Wage + */ +class Builder +{ + /** + * The Database instance. + * + * @var Database + */ + protected $database; + + /** + * The Collection instance. + * + * @var Collection + */ + protected $collection; + + /** + * The current field we are operating on. + * + * @var string + */ + protected $currentField; + + /** + * Array containing the query data. + * + * @var array + */ + protected $query = array( + 'type' => Query::TYPE_FIND, + 'distinctField' => null, + 'select' => array(), + 'sort' => array(), + 'limit' => null, + 'skip' => null, + 'group' => array( + 'keys' => null, + 'initial' => null, + 'reduce' => null, + 'options' => array(), + ), + 'hints' => array(), + 'immortal' => false, + 'snapshot' => false, + 'slaveOkay' => null, + 'eagerCursor' => false, + 'mapReduce' => array( + 'map' => null, + 'reduce' => null, + 'options' => array(), + ), + 'near' => array(), + 'new' => false, + 'upsert' => false, + 'multiple' => false, + ); + + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * Holds a Query\Expr instance used for generating query expressions using the operators. + * + * @var Query\Expr $expr + */ + protected $expr; + + /** Refresh hint */ + const HINT_REFRESH = 1; + + /** + * Create a new query builder. + * + * @param Database $database + * @param Collection $collection + */ + public function __construct(Database $database, Collection $collection, $cmd) + { + $this->database = $database; + $this->collection = $collection; + $this->expr = new Expr($cmd); + $this->cmd = $cmd; + } + + /** + * Get the type of this query. + * + * @return string $type + */ + public function getType() + { + return $this->query['type']; + } + + /** + * Set slave okay. + * + * @param bool $bool + * @return Builder + */ + public function slaveOkay($bool = true) + { + $this->query['slaveOkay'] = $bool; + return $this; + } + + /** + * Set eager cursor. + * + * @param bool $bool + * @return Builder + */ + public function eagerCursor($bool = true) + { + $this->query['eagerCursor'] = $bool; + return $this; + } + + /** + * Set snapshot. + * + * @param bool $bool + * @return Builder + */ + public function snapshot($bool = true) + { + $this->query['snapshot'] = $bool; + return $this; + } + + /** + * Set immortal. + * + * @param bool $bool + * @return Builder + */ + public function immortal($bool = true) + { + $this->query['immortal'] = $bool; + return $this; + } + + /** + * Pass a hint to the Cursor + * + * @param string $keyPattern + * @return Builder + */ + public function hint($keyPattern) + { + $this->query['hints'][] = $keyPattern; + return $this; + } + + /** + * Change the query type to find and optionally set and change the class being queried. + * + * @param string $className The Document class being queried. + * @return Builder + */ + public function find() + { + $this->query['type'] = Query::TYPE_FIND; + return $this; + } + + public function count() + { + $this->query['type'] = Query::TYPE_COUNT; + return $this; + } + + /** + * Sets a flag for the query to be executed as a findAndUpdate query query. + * + * @return Builder + */ + public function findAndUpdate() + { + $this->query['type'] = Query::TYPE_FIND_AND_UPDATE; + return $this; + } + + public function returnNew($bool = true) + { + $this->query['new'] = $bool; + return $this; + } + + public function upsert($bool = true) + { + $this->query['upsert'] = $bool; + return $this; + } + + /** + * Sets a flag for the query to be executed as a findAndUpdate query query. + * + * @return Builder + */ + public function findAndRemove() + { + $this->query['type'] = Query::TYPE_FIND_AND_REMOVE; + return $this; + } + + /** + * Change the query type to update and optionally set and change the class being queried. + * + * @return Builder + */ + public function update() + { + $this->query['type'] = Query::TYPE_UPDATE; + return $this; + } + + public function multiple($bool = true) + { + $this->query['multiple'] = $bool; + return $this; + } + + /** + * Change the query type to insert and optionally set and change the class being queried. + * + * @return Builder + */ + public function insert() + { + $this->query['type'] = Query::TYPE_INSERT; + return $this; + } + + /** + * Change the query type to remove and optionally set and change the class being queried. + * + * @return Builder + */ + public function remove() + { + $this->query['type'] = Query::TYPE_REMOVE; + return $this; + } + + /** + * Perform an operation similar to SQL's GROUP BY command + * + * @param mixed $keys + * @param array $initial + * @param string|MongoCode $reduce + * @param array $options + * @return Builder + */ + public function group($keys, array $initial, $reduce = null, array $options = array()) + { + $this->query['type'] = Query::TYPE_GROUP; + $this->query['group'] = array( + 'keys' => $keys, + 'initial' => $initial, + 'reduce' => $reduce, + 'options' => $options, + ); + return $this; + } + + /** + * The distinct method queries for a list of distinct values for the given + * field for the document being queried for. + * + * @param string $field + * @return Builder + */ + public function distinct($field) + { + $this->query['type'] = Query::TYPE_DISTINCT_FIELD; + $this->query['distinctField'] = $field; + return $this; + } + + /** + * The fields to select. + * + * @param string $fieldName + * @return Builder + */ + public function select($fieldName = null) + { + $select = func_get_args(); + foreach ($select as $fieldName) { + $this->query['select'][$fieldName] = 1; + } + return $this; + } + + /** + * The fields not to select. + * + * @param string $fieldName + * @return Builder + */ + public function exclude($fieldName = null) + { + $select = func_get_args(); + foreach ($select as $fieldName) { + $this->query['select'][$fieldName] = 0; + } + return $this; + } + + /** + * Select a slice of an embedded document. + * + * @param string $fieldName + * @param integer $skip + * @param integer $limit + * @return Builder + */ + public function selectSlice($fieldName, $skip, $limit = null) + { + $slice = $skip; + if ($limit !== null) { + $slice = array($skip, $limit); + } + $this->query['select'][$fieldName] = array($this->cmd . 'slice' => $slice); + return $this; + } + + /** + * Set the current field to operate on. + * + * @param string $field + * @return Builder + */ + public function field($field) + { + $this->currentField = $field; + $this->expr->field($field); + return $this; + } + + /** + * @param $value + * @return Builder + */ + public function equals($value) + { + $this->expr->equals($value); + return $this; + } + + /** + * Add $where javascript function to reduce result sets. + * + * @param string $javascript + * @return Builder + */ + public function where($javascript) + { + $this->expr->where($javascript); + return $this; + } + + /** + * Add a new where in criteria. + * + * @param mixed $values + * @return Builder + */ + public function in($values) + { + $this->expr->in($values); + return $this; + } + + /** + * Add where not in criteria. + * + * @param mixed $values + * @return Builder + */ + public function notIn($values) + { + $this->expr->notIn($values); + return $this; + } + + /** + * Add where not equal criteria. + * + * @param string $value + * @return Builder + */ + public function notEqual($value) + { + $this->expr->notEqual($value); + return $this; + } + + /** + * Add where greater than criteria. + * + * @param string $value + * @return Builder + */ + public function gt($value) + { + $this->expr->gt($value); + return $this; + } + + /** + * Add where greater than or equal to criteria. + * + * @param string $value + * @return Builder + */ + public function gte($value) + { + $this->expr->gte($value); + return $this; + } + + /** + * Add where less than criteria. + * + * @param string $value + * @return Builder + */ + public function lt($value) + { + $this->expr->lt($value); + return $this; + } + + /** + * Add where less than or equal to criteria. + * + * @param string $value + * @return Builder + */ + public function lte($value) + { + $this->expr->lte($value); + return $this; + } + + /** + * Add where range criteria. + * + * @param string $start + * @param string $end + * @return Builder + */ + public function range($start, $end) + { + $this->expr->range($start, $end); + return $this; + } + + /** + * Add where size criteria. + * + * @param string $size + * @return Builder + */ + public function size($size) + { + $this->expr->size($size); + return $this; + } + + /** + * Add where exists criteria. + * + * @param string $bool + * @return Builder + */ + public function exists($bool) + { + $this->expr->exists($bool); + return $this; + } + + /** + * Add where type criteria. + * + * @param string $type + * @return Builder + */ + public function type($type) + { + $this->expr->type($type); + return $this; + } + + /** + * Add where all criteria. + * + * @param mixed $values + * @return Builder + */ + public function all($values) + { + $this->expr->all($values); + return $this; + } + + /** + * Add where mod criteria. + * + * @param string $mod + * @return Builder + */ + public function mod($mod) + { + $this->expr->mod($mod); + return $this; + } + + /** + * Specify a geoNear command for this query. + * + * This method sets the "near" option for the geoNear command. The "num" + * option may be set using limit(). The "distanceMultiplier" and + * "maxDistance" options may be set using their respective builder methods. + * Additional query criteria will be assigned to the "query" option. + * + * @param string $x + * @param string $y + * @return Builder + */ + public function geoNear($x, $y) + { + $this->query['type'] = Query::TYPE_GEO_LOCATION; + $this->query['geoNear'] = array('near' => array($x, $y)); + return $this; + } + + /** + * Set the "distanceMultiplier" option for a geoNear command query. + * + * @param string $distanceMultiplier + * @return Builder + */ + public function distanceMultiplier($distanceMultiplier) + { + $this->query['geoNear']['distanceMultiplier'] = $distanceMultiplier; + return $this; + } + + /** + * Set the "maxDistance" option for a geoNear command query or add + * $maxDistance criteria to the query. + * + * If the query type is geospatial (i.e. geoNear() was called), the + * "maxDistance" command option will be set; otherwise, $maxDistance will be + * added to the current expression. + * + * @param string $maxDistance + * @return Builder + */ + public function maxDistance($maxDistance) + { + if (Query::TYPE_GEO_LOCATION === $this->query['type']) { + $this->query['geoNear']['maxDistance'] = $maxDistance; + } else { + $this->expr->maxDistance($maxDistance); + } + return $this; + } + + /** + * Set the "spherical" option for a geoNear command query. + * + * @param bool $spherical + * @return Builder + */ + public function spherical($spherical = true) + { + $this->query['geoNear']['spherical'] = $spherical; + return $this; + } + + /** + * Add $near criteria to the query. + * + * @param string $x + * @param string $y + * @return Builder + */ + public function near($x, $y) + { + $this->expr->near($x, $y); + return $this; + } + + /** + * Add $withinBox criteria to the query. + * + * @param string $x1 + * @param string $y1 + * @param string $x2 + * @param string $y2 + * @return Builder + */ + public function withinBox($x1, $y1, $x2, $y2) + { + $this->expr->withinBox($x1, $y1, $x2, $y2); + return $this; + } + + /** + * Add $withinCenter criteria to the query. + * + * @param string $x + * @param string $y + * @param string $radius + * @return Builder + */ + public function withinCenter($x, $y, $radius) + { + $this->expr->withinCenter($x, $y, $radius); + return $this; + } + + /** + * Add $withinPolygon criteria to the query. + * + * @param array $point,... Three or more point coordinate tuples + * @return Builder + */ + public function withinPolygon(/* array($x1, $y1), array($x2, $y2), ... */) + { + call_user_func_array(array($this->expr, 'withinPolygon'), func_get_args()); + return $this; + } + + /** + * Set sort. + * + * @param string $fieldName + * @param string $order + * @return Builder + */ + public function sort($fieldName, $order = null) + { + if (is_array($fieldName)) { + foreach ($fieldName as $fieldName => $order) { + $this->sort($fieldName, $order); + } + } else { + if (is_string($order)) { + $order = strtolower($order) === 'asc' ? 1 : -1; + } + $order = (int) $order; + $this->query['sort'][$fieldName] = $order; + } + return $this; + } + + /** + * Set the Document limit for the Cursor + * + * @param string $limit + * @return Builder + */ + public function limit($limit) + { + $this->query['limit'] = $limit; + return $this; + } + + /** + * Set the number of Documents to skip for the Cursor + * + * @param string $skip + * @return Builder + */ + public function skip($skip) + { + $this->query['skip'] = $skip; + return $this; + } + + /** + * Specify a map reduce operation for this query. + * + * @param string|MongoCode $map + * @param string|MongoCode $reduce + * @param array $out + * @param array $options + * @return Builder + */ + public function mapReduce($map, $reduce, array $out = array('inline' => true), array $options = array()) + { + $this->query['type'] = Query::TYPE_MAP_REDUCE; + $this->query['mapReduce'] = array( + 'map' => $map, + 'reduce' => $reduce, + 'out' => $out, + 'options' => $options + ); + return $this; + } + + /** + * Specify a map operation for this query. + * + * @param string|MongoCode $map + * @return Builder + */ + public function map($map) + { + $this->query['type'] = Query::TYPE_MAP_REDUCE; + $this->query['mapReduce']['map'] = $map; + return $this; + } + + /** + * Specify a reduce operation for this query. + * + * @param string|MongoCode $reduce + * @return Builder + * @throws BadMethodCallException if the query type is unsupported + */ + public function reduce($reduce) + { + switch ($this->query['type']) { + case Query::TYPE_MAP_REDUCE: + $this->query['mapReduce']['reduce'] = $reduce; + break; + + case Query::TYPE_GROUP: + $this->query['group']['reduce'] = $reduce; + break; + + default: + throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()'); + } + + return $this; + } + + /** + * Specify a finalize operation for this query. + * + * @param string|MongoCode $finalize + * @return Builder + */ + public function finalize($finalize) + { + switch ($this->query['type']) { + case Query::TYPE_MAP_REDUCE: + $this->query['mapReduce']['options']['finalize'] = $finalize; + break; + + case Query::TYPE_GROUP: + $this->query['group']['options']['finalize'] = $finalize; + break; + + default: + throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()'); + } + + return $this; + } + + /** + * Specify output type for map/reduce operation. + * + * @param array $out + * @return Builder + */ + public function out(array $out) + { + $this->query['mapReduce']['out'] = $out; + return $this; + } + + /** + * Specify the map reduce array of options for this query. + * + * @param array $options + * @return Builder + */ + public function mapReduceOptions(array $options) + { + $this->query['mapReduce']['options'] = $options; + return $this; + } + + /** + * Set field to value. + * + * @param mixed $value + * @param boolean $atomic + * @return Builder + */ + public function set($value, $atomic = true) + { + if ($this->query['type'] == Query::TYPE_INSERT) { + $atomic = false; + } + $this->expr->set($value, $atomic); + return $this; + } + + /** + * Increment field by the number value if field is present in the document, + * otherwise sets field to the number value. + * + * @param integer $value + * @return Builder + */ + public function inc($value) + { + $this->expr->inc($value); + return $this; + } + + /** + * Deletes a given field. + * + * @return Builder + */ + public function unsetField() + { + $this->expr->unsetField(); + return $this; + } + + /** + * Appends value to field, if field is an existing array, otherwise sets + * field to the array [value] if field is not present. If field is present + * but is not an array, an error condition is raised. + * + * @param mixed $value + * @return Builder + */ + public function push($value) + { + $this->expr->push($value); + return $this; + } + + /** + * Appends each value in valueArray to field, if field is an existing + * array, otherwise sets field to the array valueArray if field is not + * present. If field is present but is not an array, an error condition is + * raised. + * + * @param array $valueArray + * @return Builder + */ + public function pushAll(array $valueArray) + { + $this->expr->pushAll($valueArray); + return $this; + } + + /** + * Adds value to the array only if its not in the array already. + * + * @param mixed $value + * @return Builder + */ + public function addToSet($value) + { + $this->expr->addToSet($value); + return $this; + } + + /** + * Adds values to the array only they are not in the array already. + * + * @param array $values + * @return Builder + */ + public function addManyToSet(array $values) + { + $this->expr->addManyToSet($values); + return $this; + } + + /** + * Removes first element in an array + * + * @return Builder + */ + public function popFirst() + { + $this->expr->popFirst(); + return $this; + } + + /** + * Removes last element in an array + * + * @return Builder + */ + public function popLast() + { + $this->expr->popLast(); + return $this; + } + + /** + * Removes all occurrences of value from field, if field is an array. + * If field is present but is not an array, an error condition is raised. + * + * @param mixed $value + * @return Builder + */ + public function pull($value) + { + $this->expr->pull($value); + return $this; + } + + /** + * Removes all occurrences of each value in value_array from field, if + * field is an array. If field is present but is not an array, an error + * condition is raised. + * + * @param array $valueArray + * @return Builder + */ + public function pullAll(array $valueArray) + { + $this->expr->pullAll($valueArray); + return $this; + } + + /** + * Adds an "or" expression to the current query. + * + * You can create the expression using the expr() method: + * + * $qb = $this->createQueryBuilder('User'); + * $qb + * ->addOr($qb->expr()->field('first_name')->equals('Kris')) + * ->addOr($qb->expr()->field('first_name')->equals('Chris')); + * + * @param array|QueryBuilder $expression + * @return Builder + */ + public function addOr($expression) + { + $this->expr->addOr($expression); + return $this; + } + + /** + * Adds an "and" expression to the current query. + * + * You can create the expression using the expr() method: + * + * $qb = $this->createQueryBuilder('User'); + * $qb + * ->addAnd($qb->expr()->field('first_name')->equals('Kris')) + * ->addAnd($qb->expr()->field('first_name')->equals('Chris')); + * + * @param array|QueryBuilder $expression + * @return Query + */ + public function addAnd($expression) + { + $this->expr->addAnd($expression); + return $this; + } + + /** + * Adds an "elemMatch" expression to the current query. + * + * You can create the expression using the expr() method: + * + * $qb = $this->createQueryBuilder('User'); + * $qb + * ->field('phonenumbers') + * ->elemMatch($qb->expr()->field('phonenumber')->equals('6155139185')); + * + * @param array|QueryBuilder $expression + * @return Builder + */ + public function elemMatch($expression) + { + $this->expr->elemMatch($expression); + return $this; + } + + /** + * Adds a "not" expression to the current query. + * + * You can create the expression using the expr() method: + * + * $qb = $this->createQueryBuilder('User'); + * $qb->field('id')->not($qb->expr()->in(1)); + * + * @param array|QueryBuilder $expression + * @return Builder + */ + public function not($expression) + { + $this->expr->not($expression); + return $this; + } + + /** + * Create a new Query\Expr instance that can be used as an expression with the QueryBuilder + * + * @return Expr $expr + */ + public function expr() + { + return new Expr($this->cmd); + } + + public function getQueryArray() + { + return $this->expr->getQuery(); + } + + public function setQueryArray(array $query) + { + $this->expr->setQuery($query); + return $this; + } + + public function getNewObj() + { + return $this->expr->getNewObj(); + } + + public function setNewObj(array $newObj) + { + $this->expr->setNewObj($newObj); + return $this; + } + + /** + * Gets the Query executable. + * + * @param array $options + * @return Query $query + */ + public function getQuery(array $options = array()) + { + $query = $this->query; + $query['query'] = $this->expr->getQuery(); + $query['newObj'] = $this->expr->getNewObj(); + return new Query($this->database, $this->collection, $query, $options, $this->cmd); + } + + /** + * Gets an array of information about this query builder for debugging. + * + * @param string $name + * @return array $debug + */ + public function debug($name = null) + { + $debug = $this->query; + if ($name !== null) { + return $debug[$name]; + } + foreach ($debug as $key => $value) { + if ( ! $value) { + unset($debug[$key]); + } + } + return $debug; + } + + /** + * Deep clone the expression object. + */ + public function __clone() + { + $this->expr = clone $this->expr; + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Expr.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Expr.php new file mode 100644 index 00000000..ab6beaf1 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Expr.php @@ -0,0 +1,399 @@ +. + */ + +namespace Doctrine\MongoDB\Query; + +/** + * Expression builder class. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @since 1.0 + * @author Jonathan H. Wage + */ +class Expr +{ + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * The query array built by this expression class. + * + * @var string + */ + protected $query = array(); + + /** + * The new object array containing a whole new document or a query containing + * atomic operators to update a document. + * + * @var array + */ + protected $newObj = array(); + + /** + * The current field we are operating on. + * + * @var string + */ + protected $currentField; + + public function __construct($cmd) + { + $this->cmd = $cmd; + } + + public function getQuery() + { + return $this->query; + } + + public function setQuery(array $query) + { + $this->query = $query; + } + + public function getNewObj() + { + return $this->newObj; + } + + public function setNewObj(array $newObj) + { + $this->newObj = $newObj; + } + + public function getCurrentField() + { + return $this->currentField; + } + + public function field($field) + { + $this->currentField = $field; + return $this; + } + + public function equals($value, array $options = array()) + { + if ($this->currentField) { + $this->query[$this->currentField] = $value; + } else { + $this->query = $value; + } + return $this; + } + + public function where($javascript) + { + return $this->field($this->cmd . 'where')->equals($javascript); + } + + public function operator($operator, $value) + { + if ($this->currentField) { + $this->query[$this->currentField][$operator] = $value; + } else { + $this->query[$operator] = $value; + } + return $this; + } + + public function in($values) + { + return $this->operator($this->cmd . 'in', $values); + } + + public function notIn($values) + { + return $this->operator($this->cmd . 'nin', (array) $values); + } + + public function notEqual($value) + { + return $this->operator($this->cmd . 'ne', $value); + } + + public function gt($value) + { + return $this->operator($this->cmd . 'gt', $value); + } + + public function gte($value) + { + return $this->operator($this->cmd . 'gte', $value); + } + + public function lt($value) + { + return $this->operator($this->cmd . 'lt', $value); + } + + public function lte($value) + { + return $this->operator($this->cmd . 'lte', $value); + } + + public function range($start, $end) + { + return $this->operator($this->cmd . 'gte', $start)->operator($this->cmd . 'lt', $end); + } + + public function size($size) + { + return $this->operator($this->cmd . 'size', $size); + } + + public function exists($bool) + { + return $this->operator($this->cmd . 'exists', $bool); + } + + public function type($type) + { + $map = array( + 'double' => 1, + 'string' => 2, + 'object' => 3, + 'array' => 4, + 'binary' => 5, + 'undefined' => 6, + 'objectid' => 7, + 'boolean' => 8, + 'date' => 9, + 'null' => 10, + 'regex' => 11, + 'jscode' => 13, + 'symbol' => 14, + 'jscodewithscope' => 15, + 'integer32' => 16, + 'timestamp' => 17, + 'integer64' => 18, + 'minkey' => 255, + 'maxkey' => 127 + ); + if (is_string($type) && isset($map[$type])) { + $type = $map[$type]; + } + return $this->operator($this->cmd . 'type', $type); + } + + public function all($values) + { + return $this->operator($this->cmd . 'all', (array) $values); + } + + public function mod($mod) + { + return $this->operator($this->cmd . 'mod', $mod); + } + + public function near($x, $y) + { + if ($this->currentField) { + $this->query[$this->currentField][$this->cmd . 'near'] = array($x, $y); + } else { + $this->query[$this->cmd . 'near'] = array($x, $y); + } + return $this; + } + + public function maxDistance($maxDistance) + { + if ($this->currentField) { + $this->query[$this->currentField][$this->cmd . 'maxDistance'] = $maxDistance; + } else { + $this->query[$this->cmd . 'maxDistance'] = $maxDistance; + } + return $this; + } + + public function withinBox($x1, $y1, $x2, $y2) + { + if ($this->currentField) { + $this->query[$this->currentField][$this->cmd . 'within'][$this->cmd . 'box'] = array(array($x1, $y1), array($x2, $y2)); + } else { + $this->query[$this->cmd . 'within'][$this->cmd . 'box'] = array(array($x1, $y1), array($x2, $y2)); + } + return $this; + } + + public function withinCenter($x, $y, $radius) + { + if ($this->currentField) { + $this->query[$this->currentField][$this->cmd . 'within'][$this->cmd . 'center'] = array(array($x, $y), $radius); + } else { + $this->query[$this->cmd . 'within'][$this->cmd . 'center'] = array(array($x, $y), $radius); + } + return $this; + } + + public function withinPolygon(/* array($x1, $y1), array($x2, $y2), ... */) + { + if (func_num_args() < 3) { + throw new \InvalidArgumentException('Polygon must be defined by three points or more.'); + } + + if ($this->currentField) { + $this->query[$this->currentField][$this->cmd . 'within'][$this->cmd . 'polygon'] = func_get_args(); + } else { + $this->query[$this->cmd . 'within'][$this->cmd . 'polygon'] = func_get_args(); + } + return $this; + } + + public function set($value, $atomic = true) + { + $this->requiresCurrentField(); + if ($atomic === true) { + $this->newObj[$this->cmd . 'set'][$this->currentField] = $value; + } else { + if (strpos($this->currentField, '.') !== false) { + $e = explode('.', $this->currentField); + $current = &$this->newObj; + foreach ($e as $v) { + $current = &$current[$v]; + } + $current = $value; + } else { + $this->newObj[$this->currentField] = $value; + } + } + return $this; + } + + public function inc($value) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'inc'][$this->currentField] = $value; + return $this; + } + + public function unsetField() + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'unset'][$this->currentField] = 1; + return $this; + } + + public function push($value) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'push'][$this->currentField] = $value; + return $this; + } + + public function pushAll(array $valueArray) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'pushAll'][$this->currentField] = $valueArray; + return $this; + } + + public function addToSet($value) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'addToSet'][$this->currentField] = $value; + return $this; + } + + public function addManyToSet(array $values) + { + $this->requiresCurrentField(); + if ( ! isset($this->newObj[$this->cmd . 'addToSet'][$this->currentField])) { + $this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'] = array(); + } + if ( ! is_array($this->newObj[$this->cmd . 'addToSet'][$this->currentField])) { + $this->newObj[$this->cmd . 'addToSet'][$this->currentField] = array($this->cmd . 'each' => array($this->newObj[$this->cmd . 'addToSet'][$this->currentField])); + } + $this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'] = array_merge_recursive($this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'], $values); + } + + public function popFirst() + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'pop'][$this->currentField] = 1; + return $this; + } + + public function popLast() + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'pop'][$this->currentField] = -1; + return $this; + } + + public function pull($value) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'pull'][$this->currentField] = $value; + return $this; + } + + public function pullAll(array $valueArray) + { + $this->requiresCurrentField(); + $this->newObj[$this->cmd . 'pullAll'][$this->currentField] = $valueArray; + return $this; + } + + public function addOr($expression) + { + if ($expression instanceof Expr) { + $expression = $expression->getQuery(); + } + $this->query[$this->cmd . 'or'][] = $expression; + return $this; + } + + public function addAnd($expression) + { + if ($expression instanceof Expr) { + $expression = $expression->getQuery(); + } + $this->query[$this->cmd . 'and'][] = $expression; + return $this; + } + + public function elemMatch($expression) + { + if ($expression instanceof Expr) { + $expression = $expression->getQuery(); + } + return $this->operator($this->cmd . 'elemMatch', $expression); + } + + public function not($expression) + { + if ($expression instanceof Expr) { + $expression = $expression->getQuery(); + } + return $this->operator($this->cmd . 'not', $expression); + } + + private function requiresCurrentField() + { + if ( ! $this->currentField) { + throw new \LogicException('This method requires you set a current field using field().'); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Query.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Query.php new file mode 100644 index 00000000..10794bbb --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Query/Query.php @@ -0,0 +1,295 @@ +. + */ + +namespace Doctrine\MongoDB\Query; + +use Doctrine\MongoDB\Collection; +use Doctrine\MongoDB\Cursor; +use Doctrine\MongoDB\Database; +use Doctrine\MongoDB\EagerCursor; +use Doctrine\MongoDB\Iterator; +use Doctrine\MongoDB\IteratorAggregate; + +/** + * Query is responsible for executing and returning the results from queries built by the + * query builder. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @since 1.0 + * @author Jonathan H. Wage + * @author Bulat Shakirzyanov + */ +class Query implements IteratorAggregate +{ + const TYPE_FIND = 1; + const TYPE_FIND_AND_UPDATE = 2; + const TYPE_FIND_AND_REMOVE = 3; + const TYPE_INSERT = 4; + const TYPE_UPDATE = 5; + const TYPE_REMOVE = 6; + const TYPE_GROUP = 7; + const TYPE_MAP_REDUCE = 8; + const TYPE_DISTINCT_FIELD = 9; + const TYPE_GEO_LOCATION = 10; + const TYPE_COUNT = 11; + + /** + * The Database instance. + * + * @var Database + */ + protected $database; + + /** + * The Collection instance. + * + * @var Collection + */ + protected $collection; + + /** + * Array containing the query data. + * + * @var array + */ + protected $query = array(); + + /** + * Mongo command prefix + * + * @var string + */ + protected $cmd; + + /** + * @var Iterator + */ + protected $iterator; + + /** + * Query options + * + * @var array + */ + protected $options; + + public function __construct(Database $database, Collection $collection, array $query, array $options, $cmd) + { + $this->database = $database; + $this->collection = $collection; + $this->query = $query; + $this->cmd = $cmd; + $this->options = $options; + } + + /** + * Gets an array of information about this query for debugging. + * + * @param string $name + * @return array $debug + */ + public function debug($name = null) + { + $debug = $this->query['query']; + if ($name !== null) { + return $debug[$name]; + } + foreach ($debug as $key => $value) { + if ( ! $value) { + unset($debug[$key]); + } + } + return $debug; + } + + public function getQuery() + { + return $this->query; + } + + public function getType() + { + return $this->query['type']; + } + + public function getIterator() + { + if ($this->iterator === null) { + $iterator = $this->execute(); + if ($iterator !== null && !$iterator instanceof Iterator) { + throw new \BadMethodCallException('Query execution did not return an iterator. This query may not support returning iterators. '); + } + $this->iterator = $iterator; + } + return $this->iterator; + } + + public function execute() + { + switch ($this->query['type']) { + case self::TYPE_FIND: + $cursor = $this->collection->find($this->query['query'], $this->query['select']); + return $this->prepareCursor($cursor); + + case self::TYPE_FIND_AND_UPDATE: + if ($this->query['sort']) { + $this->options['sort'] = $this->query['sort']; + } + if ($this->query['select']) { + $this->options['fields'] = $this->query['select']; + } + if ($this->query['upsert']) { + $this->options['upsert'] = true; + } + if ($this->query['new']) { + $this->options['new'] = true; + } + return $this->collection->findAndUpdate($this->query['query'], $this->query['newObj'], $this->options); + + case self::TYPE_FIND_AND_REMOVE: + if ($this->query['sort']) { + $this->options['sort'] = $this->query['sort']; + } + if ($this->query['select']) { + $this->options['fields'] = $this->query['select']; + } + return $this->collection->findAndRemove($this->query['query'], $this->options); + + case self::TYPE_INSERT: + return $this->collection->insert($this->query['newObj']); + + case self::TYPE_UPDATE: + if ($this->query['upsert']) { + $this->options['upsert'] = $this->query['upsert']; + } + if ($this->query['multiple']) { + $this->options['multiple'] = $this->query['multiple']; + } + return $this->collection->update($this->query['query'], $this->query['newObj'], $this->options); + + case self::TYPE_REMOVE: + return $this->collection->remove($this->query['query'], $this->options); + + case self::TYPE_GROUP: + if (!empty($this->query['query'])) { + $this->query['group']['options']['condition'] = $this->query['query']; + } + + $options = array_merge($this->options, $this->query['group']['options']); + + return $this->collection->group($this->query['group']['keys'], $this->query['group']['initial'], $this->query['group']['reduce'], $options); + + case self::TYPE_MAP_REDUCE: + if (!isset($this->query['mapReduce']['out'])) { + $this->query['mapReduce']['out'] = array('inline' => true); + } + + $options = array_merge($this->options, $this->query['mapReduce']['options']); + $cursor = $this->collection->mapReduce($this->query['mapReduce']['map'], $this->query['mapReduce']['reduce'], $this->query['mapReduce']['out'], $this->query['query'], $options); + + if ($cursor instanceof Cursor) { + $cursor = $this->prepareCursor($cursor); + } + + return $cursor; + + case self::TYPE_DISTINCT_FIELD: + return $this->collection->distinct($this->query['distinctField'], $this->query['query'], $this->options); + + case self::TYPE_GEO_LOCATION: + if (isset($this->query['limit']) && $this->query['limit']) { + $this->options['num'] = $this->query['limit']; + } + + foreach (array('distanceMultiplier', 'maxDistance', 'spherical') as $key) { + if (isset($this->query['geoNear'][$key]) && $this->query['geoNear'][$key]) { + $this->options[$key] = $this->query['geoNear'][$key]; + } + } + + return $this->collection->near($this->query['geoNear']['near'], $this->query['query'], $this->options); + + case self::TYPE_COUNT: + return $this->collection->count($this->query['query']); + } + } + + protected function prepareCursor(Cursor $cursor) + { + $cursor->limit($this->query['limit']); + $cursor->skip($this->query['skip']); + $cursor->sort($this->query['sort']); + $cursor->immortal($this->query['immortal']); + + if (null !== $this->query['slaveOkay']) { + $cursor->slaveOkay($this->query['slaveOkay']); + } + + if ($this->query['snapshot']) { + $cursor->snapshot(); + } + + foreach ($this->query['hints'] as $keyPattern) { + $cursor->hint($keyPattern); + } + + if ($this->query['eagerCursor'] === true) { + $cursor = new EagerCursor($cursor); + } + + return $cursor; + } + + /** + * Count the number of results for this query. + * + * @param bool $all + * @return integer $count + */ + public function count($all = false) + { + return $this->getIterator()->count($all); + } + + /** + * Execute the query and get a single result + * + * @return object $document The single document. + */ + public function getSingleResult() + { + return $this->getIterator()->getSingleResult(); + } + + /** + * Iterator over the query using the Cursor. + * + * @return Cursor $cursor + */ + public function iterate() + { + return $this->getIterator(); + } + + /** @inheritDoc */ + public function toArray() + { + return $this->getIterator()->toArray(); + } +} diff --git a/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Util/ReadPreference.php b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Util/ReadPreference.php new file mode 100644 index 00000000..9c004df0 --- /dev/null +++ b/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Util/ReadPreference.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\MongoDB\Util; + +/** + * Utility class for converting read preferences. + * + * This is necessary for versions of the driver <=1.3.2, where values returned + * by getReadPreference() are not consistent with those expected by + * setReadPreference(). See: https://jira.mongodb.org/browse/PHP-638. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 1.0 + * @author Jeremy Mikola + */ +final class ReadPreference +{ + /** + * Read preference types. + * + * The indexes correspond to the numeric values for each read preference + * returned by getReadPreference() methods, while the values correspond to + * the string constants expected by setReadPreference() methods. + */ + private static $types = array( + \MongoClient::RP_PRIMARY, + \MongoClient::RP_PRIMARY_PREFERRED, + \MongoClient::RP_SECONDARY, + \MongoClient::RP_SECONDARY_PREFERRED, + \MongoClient::RP_NEAREST, + ); + + /** + * Private constructor (prevents instantiation) + */ + private function __construct() {} + + /** + * Converts a numeric type returned by getReadPreference() methods to the + * constant accepted by setReadPreference() methods. + * + * @param integer $type + * @return string + */ + public static function convertNumericType($type) + { + if (!isset(self::$types[$type])) { + throw new \InvalidArgumentException('Unknown numeric read preference type: ' . $type); + } + + return self::$types[$type]; + } + + /** + * Converts tag sets returned by getReadPreference() methods to the format + * accepted by setReadPreference() methods. + * + * Example input: + * + * [['dc:east', 'use:reporting'], ['dc:west'], []] + * + * Example output: + * + * [['dc' => 'east', 'use' => 'reporting'], ['dc' => 'west'], []] + * + * @param array $tagSets + * @return array + */ + public static function convertTagSets(array $tagSets) + { + return array_map(function(array $tagSet) { + /* If the tag set does not contain a zeroth element, or that element + * does not contain a colon character, we can assume this tag set is + * already in the format expected by setReadPreference(). + */ + if (!isset($tagSet[0]) || false === strpos($tagSet[0], ':')) { + return $tagSet; + } + + $result = array(); + + foreach ($tagSet as $tagAndValue) { + list($tag, $value) = explode(':', $tagAndValue, 2); + $result[$tag] = $value; + } + + return $result; + }, $tagSets); + } +} diff --git a/vendor/doctrine/mongodb/phpunit.xml.dist b/vendor/doctrine/mongodb/phpunit.xml.dist new file mode 100644 index 00000000..f7987347 --- /dev/null +++ b/vendor/doctrine/mongodb/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/MongoDB + + + \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/BaseTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/BaseTest.php new file mode 100644 index 00000000..422f2b99 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/BaseTest.php @@ -0,0 +1,32 @@ +setLoggerCallable(function($msg) {}); + $this->conn = new Connection(null, array(), $config); + } + + public function tearDown() + { + $collections = $this->conn->selectDatabase(self::$dbName)->listCollections(); + foreach ($collections as $collection) { + $collection->drop(); + } + + $this->conn->close(); + unset($this->conn); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CollectionTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CollectionTest.php new file mode 100644 index 00000000..e7710ab5 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CollectionTest.php @@ -0,0 +1,572 @@ +getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->any()) + ->method('getName') + ->will($this->returnValue('collection')); + + $mockDatabase = $this->getMockDatabase(); + $mockDatabase->expects($this->once()) + ->method('getName') + ->will($this->returnValue('db')); + + $called = false; + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase, function($msg) use (&$called) { + $called = $msg; + }); + $coll->log(array('test' => 'test')); + $this->assertEquals(array('collection' => 'collection', 'db' => 'db', 'test' => 'test'), $called); + } + + public function testBatchInsert() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mockDatabase = $this->getMockDatabase(); + + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $doc = array(); + $result = $coll->batchInsert($doc, array()); + $this->assertEquals(array(), $result); + } + + public function testUpdate() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('update') + ->with(array(), array(), array()) + ->will($this->returnValue(array())); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->update(array(), array(), array()); + $this->assertEquals(array(), $result); + } + + public function testFind() + { + $mockConnection = $this->getMockConnection(); + $mockMongoCursor = $this->getMockMongoCursor(); + + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('find') + ->with(array(), array()) + ->will($this->returnValue($mockMongoCursor)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->find(array(), array()); + $this->assertEquals($mockMongoCursor, $result->getMongoCursor()); + } + + public function testFindOne() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('findOne') + ->with(array(), array()) + ->will($this->returnValue(array())); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->findOne(array(), array()); + $this->assertEquals(array(), $result); + } + + public function testFindAndRemove() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->any()) + ->method('getName') + ->will($this->returnValue('coll_name')); + + $query = array('name' => 'jon'); + $options = array('safe' => true); + $command = array( + 'findandmodify' => 'coll_name', + 'safe' => true, + 'query' => $query, + 'remove' => true, + ); + + $document = array('_id' => new \MongoId(), 'test' => 'cool'); + $mockDatabase = $this->getMockDatabase(); + $mockDatabase->expects($this->once()) + ->method('command') + ->with($command) + ->will($this->returnValue(array('value' => $document))); + + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->findAndRemove($query, $options); + $this->assertEquals($document, $result); + } + + public function testFindAndModify() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->any()) + ->method('getName') + ->will($this->returnValue('coll_name')); + + $query = array('name' => 'jon'); + $newObj = array('name' => 'ok'); + $options = array('safe' => true); + $command = array( + 'findandmodify' => 'coll_name', + 'query' => $query, + 'safe' => true, + 'update' => array( + 'name' => 'ok' + ), + ); + + $document = array('_id' => new \MongoId(), 'test' => 'cool'); + $mockDatabase = $this->getMockDatabase(); + $mockDatabase->expects($this->once()) + ->method('command') + ->with($command) + ->will($this->returnValue(array('value' => $document))); + + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->findAndUpdate($query, $newObj, $options); + $this->assertEquals($document, $result); + } + + public function testCount() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('count') + ->with(array(), 0, 0) + ->will($this->returnValue(1)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->count(array(), 0, 0); + $this->assertEquals(1, $result); + } + + public function testCreateDBRef() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('createDBRef') + ->with(array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->createDBRef(array()); + $this->assertEquals(true, $result); + } + + public function testDeleteIndex() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('deleteIndex') + ->with(array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->deleteIndex(array()); + $this->assertEquals(true, $result); + } + + public function testDeleteIndexes() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('deleteIndexes') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->deleteIndexes(); + $this->assertEquals(true, $result); + } + + public function testDrop() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('drop') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->drop(); + $this->assertEquals(true, $result); + } + + public function testEnsureIndex() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('ensureIndex') + ->with(array(), array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->ensureIndex(array(), array()); + $this->assertEquals(true, $result); + } + + public function testGetDBRef() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('getDBRef') + ->with(array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->getDBRef(array()); + $this->assertEquals(true, $result); + } + + public function testGetIndexInfo() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('getIndexInfo') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->getIndexInfo(); + $this->assertEquals(true, $result); + } + + public function testIsFieldIndexedTrue() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('getIndexInfo') + ->will($this->returnValue(array(array('key' => array('test' => 1))))); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->isFieldIndexed('test'); + $this->assertEquals(true, $result); + } + + public function testIsFieldIndexedFalse() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('getIndexInfo') + ->will($this->returnValue(array(array('key' => array('test' => 1))))); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->isFieldIndexed('doesnt-exist'); + $this->assertEquals(false, $result); + } + + public function testGetName() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('getName') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->getName(); + $this->assertEquals(true, $result); + } + + public function testGroupWithNonEmptyOptionsArray() + { + $expectedOptions = array( + 'condition' => array(), + 'finalize' => new \MongoCode(''), + ); + + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('group') + ->with(array(), array(), $this->isInstanceOf('MongoCode'), $this->equalTo($expectedOptions)) + ->will($this->returnValue(array())); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->group(array(), array(), '', array('condition' => array(), 'finalize' => '')); + $this->assertEquals(new \Doctrine\MongoDB\ArrayIterator(array()), $result); + } + + public function testGroupWithEmptyOptionsArray() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('group') + ->with(array(), array(), $this->isInstanceOf('MongoCode')) + ->will($this->returnValue(array())); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->group(array(), array(), ''); + $this->assertEquals(new \Doctrine\MongoDB\ArrayIterator(array()), $result); + } + + public function testInsert() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('insert') + ->with(array(), array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $document = array(); + $result = $coll->insert($document, array()); + $this->assertEquals(true, $result); + } + + public function testRemove() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('remove') + ->with(array(), array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->remove(array(), array()); + $this->assertEquals(true, $result); + } + + public function testSave() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('save') + ->with(array(), array()) + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $document = array(); + $result = $coll->save($document, array()); + $this->assertArrayHasKeyValue(array('ok' => 1.0), $result); + } + + public function testGetSetSlaveOkay() + { + if (version_compare(phpversion('mongo'), '1.3.0', '>=')) { + $this->markTestSkipped('This test is not applicable to driver versions >= 1.3.0'); + } + + $mongoCollection = $this->getMockMongoCollection(); + + $mongoCollection->expects($this->once()) + ->method('getSlaveOkay') + ->will($this->returnValue(false)); + + $mongoCollection->expects($this->once()) + ->method('setSlaveOkay') + ->with(true) + ->will($this->returnValue(false)); + + $collection = $this->getTestCollection($this->getMockConnection(), $mongoCollection, $this->getMockDatabase()); + + $this->assertEquals(false, $collection->getSlaveOkay()); + $this->assertEquals(false, $collection->setSlaveOkay(true)); + } + + public function testGetSetSlaveOkayReadPreferences() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $mongoCollection = $this->getMockMongoCollection(); + + $mongoCollection->expects($this->never())->method('getSlaveOkay'); + $mongoCollection->expects($this->never())->method('setSlaveOkay'); + + $mongoCollection->expects($this->exactly(2)) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => 0, + 'type_string' => 'primary', + ))); + + $mongoCollection->expects($this->once()) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED); + + $collection = $this->getTestCollection($this->getMockConnection(), $mongoCollection, $this->getMockDatabase()); + + $this->assertEquals(false, $collection->setSlaveOkay(true)); + } + + public function testSetSlaveOkayPreservesReadPreferenceTags() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $mongoCollection = $this->getMockMongoCollection(); + + $mongoCollection->expects($this->exactly(2)) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => 1, + 'type_string' => 'primary preferred', + 'tagsets' => array(array('dc:east')), + ))); + + $mongoCollection->expects($this->once()) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED, array(array('dc' => 'east'))) + ->will($this->returnValue(false)); + + $collection = $this->getTestCollection($this->getMockConnection(), $mongoCollection, $this->getMockDatabase()); + + $this->assertEquals(true, $collection->setSlaveOkay(true)); + } + + public function testValidate() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('validate') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $result = $coll->validate(); + $this->assertEquals(true, $result); + } + + public function testToString() + { + $mockConnection = $this->getMockConnection(); + $mongoCollection = $this->getMockMongoCollection(); + $mongoCollection->expects($this->once()) + ->method('__toString') + ->will($this->returnValue(true)); + + $mockDatabase = $this->getMockDatabase(); + $coll = $this->getTestCollection($mockConnection, $mongoCollection, $mockDatabase); + $document = array(); + $result = $coll->__toString(); + $this->assertEquals(true, $result); + } + + private function getMockMongoCursor() + { + return $this->getMock('MongoCursor', array(), array(), '', false, false); + } + + private function getMockMongoCollection() + { + return $this->getMock('MongoCollection', array(), array(), '', false, false); + } + + private function getMockMongoDB() + { + return $this->getMock('MongoDB', array(), array(), '', false, false); + } + + private function getMockDatabase() + { + return $this->getMock('Doctrine\MongoDB\Database', array(), array(), '', false, false); + } + + private function getMockConnection() + { + return $this->getMock('Doctrine\MongoDB\Connection', array(), array(), '', false, false); + } + + private function getTestCollection(Connection $connection, MongoCollection $mongoCollection, Database $db, $loggerCallable = null) + { + if (null === $loggerCallable) { + $collection = new TestCollectionStub($connection, $mongoCollection->getName(), $db, new EventManager(), '$'); + $collection->setMongoCollection($mongoCollection); + return $collection; + } + $collection = new TestLoggableCollectionStub($connection, $mongoCollection->getName(), $db, new EventManager(), '$', $loggerCallable); + $collection->setMongoCollection($mongoCollection); + return $collection; + } + + private function assertArrayHasKeyValue($expected, $array, $message = '') + { + foreach ((array) $expected as $key => $value) { + $this->assertArrayHasKey($key, $expected, $message); + $this->assertEquals($value, $expected[$key], $message); + } + } +} + +class TestLoggableCollectionStub extends LoggableCollection +{ + public function setMongoCollection($mongoCollection) + { + $this->mongoCollection = $mongoCollection; + } + + public function getMongoCollection() + { + return $this->mongoCollection; + } +} + +class TestCollectionStub extends Collection +{ + public function setMongoCollection($mongoCollection) + { + $this->mongoCollection = $mongoCollection; + } + + public function getMongoCollection() + { + return $this->mongoCollection; + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/ConnectionTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/ConnectionTest.php new file mode 100644 index 00000000..5e4d33e2 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/ConnectionTest.php @@ -0,0 +1,200 @@ +=')) { + $this->markTestSkipped('This test is not applicable to driver versions >= 1.3.0'); + } + + $conn = new Connection(); + $this->assertNull($conn->getMongo()); + $conn->initialize(); + $this->assertInstanceOf('Mongo', $conn->getMongo()); + } + + public function testInitializeMongoClient() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $conn = new Connection(); + $this->assertNull($conn->getMongo()); + $conn->initialize(); + $this->assertInstanceOf('MongoClient', $conn->getMongo()); + } + + public function testLog() + { + $conn = new Connection(); + $called = false; + $conn->getConfiguration()->setLoggerCallable(function($msg) use (&$called) { + $called = $msg; + }); + $conn->log(array('test')); + $this->assertEquals(array('test'), $called); + } + + public function testSetMongo() + { + if (version_compare(phpversion('mongo'), '1.3.0', '>=')) { + $this->markTestSkipped('This test is not applicable to driver versions >= 1.3.0'); + } + + $mongo = $this->getMockBuilder('Mongo') + ->disableOriginalConstructor() + ->getMock(); + + $conn = new Connection(); + $conn->setMongo($mongo); + $this->assertSame($mongo, $conn->getMongo()); + } + + public function testSetMongoClient() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $mongoClient = $this->getMockBuilder('MongoClient') + ->disableOriginalConstructor() + ->getMock(); + + $conn = new Connection(); + $conn->setMongo($mongoClient); + $this->assertSame($mongoClient, $conn->getMongo()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetMongoShouldThrowExceptionForInvalidArgument() + { + $mongoDB = $this->getMockBuilder('MongoDB') + ->disableOriginalConstructor() + ->getMock(); + + $conn = new Connection(); + $conn->setMongo($mongoDB); + } + + public function testClose() + { + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)); + $conn = $this->getTestConnection($mockMongo); + $result = $conn->close(); + $this->assertTrue($result); + } + + public function testConnect() + { + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('connect') + ->will($this->returnValue(true)); + $conn = $this->getTestConnection($mockMongo); + $result = $conn->connect(); + $this->assertTrue($result); + } + + public function testDropDatabase() + { + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('dropDB') + ->with('test') + ->will($this->returnValue(true)); + $conn = $this->getTestConnection($mockMongo); + $result = $conn->dropDatabase('test'); + $this->assertTrue($result); + } + + public function testListDatabases() + { + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('listDBs') + ->will($this->returnValue(true)); + $conn = $this->getTestConnection($mockMongo); + $result = $conn->listDatabases(); + $this->assertTrue($result); + } + + public function testSelectCollection() + { + $mockMongoCollection = $this->getMockMongoCollection(); + + $mockMongoDB = $this->getMockMongoDB(); + $mockMongoDB->expects($this->once()) + ->method('selectCollection') + ->with('coll') + ->will($this->returnValue($mockMongoCollection)); + + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('selectDB') + ->with('db') + ->will($this->returnValue($mockMongoDB)); + + $conn = $this->getTestConnection($mockMongo); + $result = $conn->selectCollection('db', 'coll'); + $this->assertSame($mockMongoCollection, $result->getMongoCollection()); + } + + public function testSelectDatabase() + { + $mockMongoDB = $this->getMockMongoDB(); + + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('selectDB') + ->with('db') + ->will($this->returnValue($mockMongoDB)); + + $conn = $this->getTestConnection($mockMongo); + $result = $conn->selectDatabase('db'); + $this->assertSame($mockMongoDB, $result->getMongoDB()); + } + + public function testToString() + { + $mockMongo = $this->getMockMongo(); + $mockMongo->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('Test')); + + $conn = $this->getTestConnection($mockMongo); + $this->assertEquals('Test', (string) $conn); + } + + private function getTestConnection(Mongo $mongo) + { + return new \Doctrine\MongoDB\Connection($mongo); + } + + private function getMockMongo() + { + return $this->getMock('Mongo', array(), array(), '', false, false); + } + + private function getMockMongoDB() + { + return $this->getMock('MongoDB', array(), array(), '', false, false); + } + + private function getMockMongoCollection() + { + return $this->getMock('MongoCollection', array(), array(), '', false, false); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Constraint/ArrayHasValueUnderKey.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Constraint/ArrayHasValueUnderKey.php new file mode 100644 index 00000000..211042cf --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Constraint/ArrayHasValueUnderKey.php @@ -0,0 +1,43 @@ +key = $key; + $this->value = $value; + } + + public function evaluate($other, $description = '', $returnResult = FALSE) + { + if (!isset($other[$this->key])) { + return false; + } + + if ($other[$this->key] != $this->value) { + return false; + } + + return true; + } + + public function toString() + { + return sprintf('has the value %s under key %s', + \PHPUnit_Util_Type::toString($this->value), + \PHPUnit_Util_Type::toString($this->key) + ); + } + + protected function customFailureDescription($other, $description, $not) + { + return sprintf( + 'Failed asserting that an array %s.', + $this->toString() + ); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorFunctionalTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorFunctionalTest.php new file mode 100644 index 00000000..6570ff79 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorFunctionalTest.php @@ -0,0 +1,44 @@ +conn->selectDatabase(self::$dbName); + $coll = $db->selectCollection('users'); + + $doc1 = array('test' => 'test'); + $coll->insert($doc1); + + $doc2 = array('test' => 'test'); + $coll->insert($doc2); + + $cursor = $coll->find(array('test' => 'test')); + $cursor->limit(1); + + $this->assertEquals(1, $cursor->count(true)); + $this->assertEquals(2, $cursor->count()); + + $cursor->recreate(); + $this->assertEquals(1, $cursor->count(true)); + $this->assertEquals(2, $cursor->count()); + } + + public function testGetSingleResult() + { + $db = $this->conn->selectDatabase(self::$dbName); + $coll = $db->selectCollection('users'); + + $doc1 = array('test' => 'test', 'doc' => 1); + $coll->insert($doc1); + + $doc2 = array('test' => 'test', 'doc' => 2); + $coll->insert($doc2); + + $cursor = $coll->find(array('test' => 'test')); + $doc = $cursor->getSingleResult(); + $this->assertEquals(array('_id' => $doc1['_id'], 'test' => 'test', 'doc' => 1), $doc); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorTest.php new file mode 100644 index 00000000..8bbadfb8 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/CursorTest.php @@ -0,0 +1,212 @@ +doc1 = array('name' => 'A'); + $this->doc2 = array('name' => 'B'); + $this->doc3 = array('name' => 'C'); + + $collection = $this->conn->selectCollection(self::$dbName, 'docs'); + $collection->insert($this->doc1); + $collection->insert($this->doc2); + $collection->insert($this->doc3); + + $this->cursor = $collection->createQueryBuilder()->getQuery()->execute(); + $this->cursor->sort(array('name' => 1)); + } + + /** + * @covers Doctrine\MongoDB\Cursor::getSingleResult + */ + public function testGetSingleResult() + { + $this->assertEquals($this->doc1, $this->cursor->getSingleResult()); + } + + /** + * @covers Doctrine\MongoDB\Cursor::getSingleResult + */ + public function testCursorIsResetAfterGetSingleResult() + { + $this->assertEquals($this->doc1, $this->cursor->getSingleResult()); + + // Make sure limit is restored and cursor is rewound + $expected = array($this->doc1, $this->doc2, $this->doc3); + $actual = array(); + foreach ($this->cursor as $entry) { + $actual[] = $entry; + } + $this->assertEquals($expected, $actual); + } + + /** + * @covers Doctrine\MongoDB\Cursor::getSingleResult + */ + public function testGetSingleResultReturnsNull() + { + $collection = $this->conn->selectCollection(self::$dbName, 'tmp'); + $collection->remove(array()); + $cursor = $collection->createQueryBuilder()->getQuery()->execute(); + $this->assertNull($cursor->getSingleResult()); + } + + public function testToArray() + { + $this->assertEquals( + array( + (string) $this->doc1['_id'] => $this->doc1, + (string) $this->doc2['_id'] => $this->doc2, + (string) $this->doc3['_id'] => $this->doc3 + ), + $this->cursor->toArray() + ); + } + + public function testToArrayWithoutKeys() + { + $this->assertEquals(array($this->doc1, $this->doc2, $this->doc3), $this->cursor->toArray(false)); + } + + public function testSlaveOkay() + { + if (version_compare(phpversion('mongo'), '1.3.0', '>=')) { + $this->markTestSkipped('This test is not applicable to driver versions >= 1.3.0'); + } + + $mongoCursor = $this->getMockMongoCursor(); + + $mongoCursor->expects($this->at(0)) + ->method('slaveOkay') + ->with(true); + + $mongoCursor->expects($this->at(1)) + ->method('slaveOkay') + ->with(false); + + $cursor = $this->getTestCursor($this->getMockConnection(), $this->getMockCollection(), $mongoCursor); + + $cursor->slaveOkay(true); + $cursor->slaveOkay(false); + } + + public function testSlaveOkayNoopWithoutReadPreferences() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + if (method_exists('MongoCursor', 'setReadPreference')) { + $this->markTestSkipped('This test is not applicable to drivers with MongoCursor::setReadPreference()'); + } + + $mongoCursor = $this->getMockMongoCursor(); + + $mongoCursor->expects($this->never()) + ->method('slaveOkay'); + + $mongoCursor->expects($this->never()) + ->method('setReadPreference'); + + $cursor = $this->getTestCursor($this->getMockConnection(), $this->getMockCollection(), $mongoCursor); + + $cursor->slaveOkay(true); + $cursor->slaveOkay(false); + } + + public function testSlaveOkayReadPreferences() + { + if (!method_exists('MongoCursor', 'setReadPreference')) { + $this->markTestSkipped('This test is not applicable to drivers without MongoCursor::setReadPreference()'); + } + + $mongoCursor = $this->getMockMongoCursor(); + + $mongoCursor->expects($this->never())->method('slaveOkay'); + + $mongoCursor->expects($this->once()) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => \MongoClient::RP_PRIMARY, + ))); + + $mongoCursor->expects($this->at(1)) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED, array()); + + $mongoCursor->expects($this->at(2)) + ->method('setReadPreference') + ->with(\MongoClient::RP_PRIMARY); + + $cursor = $this->getTestCursor($this->getMockConnection(), $this->getMockCollection(), $mongoCursor); + + $cursor->slaveOkay(true); + $cursor->slaveOkay(false); + } + + public function testSlaveOkayPreservesReadPreferenceTags() + { + if (!method_exists('MongoCursor', 'setReadPreference')) { + $this->markTestSkipped('This test is not applicable to drivers without MongoCursor::setReadPreference()'); + } + + $mongoCursor = $this->getMockMongoCursor(); + + $mongoCursor->expects($this->once()) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => \MongoClient::RP_PRIMARY_PREFERRED, + 'tagsets' => array(array('dc' => 'east')), + ))); + + $mongoCursor->expects($this->once()) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED, array(array('dc' => 'east'))) + ->will($this->returnValue(false)); + + $cursor = $this->getTestCursor($this->getMockConnection(), $this->getMockCollection(), $mongoCursor); + + $cursor->slaveOkay(true); + } + + private function getMockMongoCursor() + { + return $this->getMockBuilder('MongoCursor') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockCollection() + { + return $this->getMockBuilder('Doctrine\MongoDB\Collection') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockConnection() + { + return $this->getMockBuilder('Doctrine\MongoDB\Connection') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getTestCursor(Connection $connection, Collection $collection, \MongoCursor $mongoCursor) + { + return new Cursor($connection, $collection, $mongoCursor); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/DatabaseTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/DatabaseTest.php new file mode 100644 index 00000000..2ce14a73 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/DatabaseTest.php @@ -0,0 +1,130 @@ +connection = $this->getMockConnection(); + $this->mongo = $this->getMockMongo(); + $this->mongodb = $this->getMockMongoDB(); + + $this->connection->expects($this->any()) + ->method('getMongo') + ->will($this->returnValue($this->mongo)); + + $this->mongo->expects($this->any()) + ->method('selectDB') + ->will($this->returnValue($this->mongodb)); + } + + public function testGetSetSlaveOkay() + { + if (version_compare(phpversion('mongo'), '1.3.0', '>=')) { + $this->markTestSkipped('This test is not applicable to driver versions >= 1.3.0'); + } + + $this->mongodb->expects($this->once()) + ->method('getSlaveOkay') + ->will($this->returnValue(false)); + + $this->mongodb->expects($this->once()) + ->method('setSlaveOkay') + ->with(true) + ->will($this->returnValue(false)); + + $database = new Database($this->connection, 'test', $this->getMockEventManager(), '$'); + + $this->assertEquals(false, $database->getSlaveOkay()); + $this->assertEquals(false, $database->setSlaveOkay(true)); + } + + public function testGetSetSlaveOkayReadPreferences() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $this->mongodb->expects($this->never())->method('getSlaveOkay'); + $this->mongodb->expects($this->never())->method('setSlaveOkay'); + + $this->mongodb->expects($this->exactly(2)) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => 0, + 'type_string' => 'primary', + ))); + + $this->mongodb->expects($this->once()) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED) + ->will($this->returnValue(false)); + + $database = new Database($this->connection, 'test', $this->getMockEventManager(), '$'); + + $this->assertEquals(false, $database->setSlaveOkay(true)); + } + + public function testSetSlaveOkayPreservesReadPreferenceTags() + { + if (version_compare(phpversion('mongo'), '1.3.0', '<')) { + $this->markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + + $this->mongodb->expects($this->exactly(2)) + ->method('getReadPreference') + ->will($this->returnValue(array( + 'type' => 1, + 'type_string' => 'primary preferred', + 'tagsets' => array(array('dc:east')), + ))); + + $this->mongodb->expects($this->once()) + ->method('setReadPreference') + ->with(\MongoClient::RP_SECONDARY_PREFERRED, array(array('dc' => 'east'))) + ->will($this->returnValue(false)); + + $database = new Database($this->connection, 'test', $this->getMockEventManager(), '$'); + + $this->assertEquals(true, $database->setSlaveOkay(true)); + } + + private function getMockConnection() + { + return $this->getMockBuilder('Doctrine\MongoDB\Connection') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockEventManager() + { + return $this->getMockBuilder('Doctrine\Common\EventManager') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockMongo() + { + return $this->getMockBuilder('Mongo') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getMockMongoDB() + { + return $this->getMockBuilder('MongoDB') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EagerCursorTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EagerCursorTest.php new file mode 100644 index 00000000..e331f5f7 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EagerCursorTest.php @@ -0,0 +1,60 @@ +document = array('test' => 'test'); + $this->conn->selectCollection(self::$dbName, 'users')->insert($this->document); + + $qb = $this->conn->selectCollection(self::$dbName, 'users')->createQueryBuilder(); + $qb->eagerCursor(true); + $this->test = $qb->getQuery()->execute(); + } + + public function testEagerCursor() + { + $this->assertInstanceOf('Doctrine\MongoDB\EagerCursor', $this->test); + } + + public function testIsInitialized() + { + $this->assertFalse($this->test->isInitialized()); + $this->test->initialize(); + $this->assertTrue($this->test->isInitialized()); + } + + public function testCount() + { + $this->assertEquals(1, count($this->test)); + } + + public function testGetSingleResult() + { + $this->assertEquals($this->document, $this->test->getSingleResult()); + } + + public function testToArray() + { + $this->assertEquals(array((string) $this->document['_id'] => $this->document), $this->test->toArray()); + } + + public function testRewind() + { + $this->test->toArray(); + $this->assertFalse($this->test->next()); + $this->test->rewind(); + $this->assertEquals($this->document, $this->test->current()); + $this->assertFalse($this->test->next()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EventTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EventTest.php new file mode 100644 index 00000000..aa4196fd --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/EventTest.php @@ -0,0 +1,26 @@ +addEventListener(\Doctrine\MongoDB\Events::preConnect, $listener); + + $connection = new Connection(null, array(), null, $manager); + $connection->initialize(); + } +} + +class ListenerStub { + function preConnect() {} +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/FunctionalTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/FunctionalTest.php new file mode 100644 index 00000000..5657a856 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/FunctionalTest.php @@ -0,0 +1,124 @@ +conn->selectDatabase(self::$dbName); + $coll = $db->createCollection('test'); + $criteria = array(); + $newObj = array('$set' => array('test' => 'test')); + $coll->upsert($criteria, $newObj); + + $check = $coll->findOne(); + + $this->assertNotNull($coll->findOne()); + + $coll->upsert(array('_id' => $check['_id']), array('$set' => array('boo' => 'test'))); + $this->assertEquals(1, $coll->find()->count()); + $check = $coll->findOne(); + $this->assertTrue(isset($check['boo'])); + } + + public function testMapReduce() + { + $data = array( + array( + 'username' => 'jones', + 'likes' => 20, + 'text' => 'Hello world!' + ), + array( + 'username' => 'bob', + 'likes' => 100, + 'text' => 'Hello world!' + ), + array( + 'username' => 'bob', + 'likes' => 100, + 'text' => 'Hello world!' + ), + ); + + $db = $this->conn->selectDatabase(self::$dbName); + $coll = $db->createCollection('test'); + $coll->batchInsert($data); + + $map = 'function() { + emit(this.username, { count: 1, likes: this.likes }); + }'; + + $reduce = 'function(key, values) { + var result = {count: 0, likes: 0}; + + values.forEach(function(value) { + result.count += value.count; + result.likes += value.likes; + }); + + return result; + }'; + + $finalize = 'function (key, value) { value.test = "test"; return value; }'; + + $db = $this->conn->selectDatabase(self::$dbName); + $coll = $db->selectCollection('test'); + $qb = $coll->createQueryBuilder() + ->map($map)->reduce($reduce)->finalize($finalize); + $query = $qb->getQuery(); + $results = $query->execute(); + $this->assertEquals(2, $results->count()); + $result = $results->getSingleResult(); + $this->assertEquals(array('count' => 2.0, 'likes' => 200.0, 'test' => 'test'), $result['value']); + } + + public function testIsFieldIndexed() + { + $db = $this->conn->selectDatabase(self::$dbName); + + $coll = $db->selectCollection('users'); + $this->assertFalse($coll->isFieldIndexed('test')); + $coll->ensureIndex(array('test' => 1)); + $this->assertTrue($coll->isFieldIndexed('test')); + } + + public function testFunctional() + { + $db = $this->conn->selectDatabase(self::$dbName); + + $coll = $db->selectCollection('users'); + + $document = array('test' => 'jwage'); + $coll->insert($document); + + $coll->update(array('_id' => $document['_id']), array('$set' => array('test' => 'jon'))); + + $cursor = $coll->find(); + $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $cursor); + } + + public function testFunctionalGridFS() + { + $db = $this->conn->selectDatabase(self::$dbName); + $files = $db->getGridFS('files'); + $file = array( + 'title' => 'test file', + 'testing' => 'ok', + 'file' => new GridFSFile(__DIR__.'/FunctionalTest.php') + ); + $files->insert($file, array('safe' => true)); + + $this->assertTrue(isset($file['_id'])); + + $path = __DIR__.'/BaseTest.php'; + $files->update(array('_id' => $file['_id']), array('$set' => array('title' => 'test', 'file' => new GridFSFile($path)))); + + $file = $files->find()->getSingleResult(); + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $file['file']); + $this->assertEquals(file_get_contents($path), $file['file']->getBytes()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSFileTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSFileTest.php new file mode 100644 index 00000000..f6d0fd48 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSFileTest.php @@ -0,0 +1,164 @@ +assertFalse($file->isDirty()); + $file->isDirty(true); + $this->assertTrue($file->isDirty()); + $file->isDirty(false); + $this->assertFalse($file->isDirty()); + } + + public function testSetAndGetBytes() + { + $file = new GridFSFile(); + $file->setBytes('bytes'); + $this->assertTrue($file->isDirty()); + $this->assertTrue($file->hasUnpersistedBytes()); + $this->assertFalse($file->hasUnpersistedFile()); + $this->assertEquals('bytes', $file->getBytes()); + } + + public function testSetAndGetFilename() + { + $file = new GridFSFile(); + $this->assertFalse($file->isDirty()); + $file->setFilename(__FILE__); + $this->assertTrue($file->isDirty()); + $this->assertFalse($file->hasUnpersistedBytes()); + $this->assertTrue($file->hasUnpersistedFile()); + $this->assertEquals(__FILE__, $file->getFilename()); + } + + public function testSetAndGetMongoGridFSFile() + { + $mongoGridFSFile = $this->getMockMongoGridFSFile(); + + $file = new GridFSFile(); + $file->setMongoGridFSFile($mongoGridFSFile); + $this->assertSame($mongoGridFSFile, $file->getMongoGridFSFile()); + } + + public function testGetBytesWithSetBytes() + { + $file = new GridFSFile(); + $file->setBytes('bytes'); + $this->assertEquals('bytes', $file->getBytes()); + } + + public function testGetBytesWithSetFilename() + { + $file = new GridFSFile(); + $file->setFilename(__FILE__); + $this->assertStringEqualsFile(__FILE__, $file->getBytes()); + } + + public function testGetBytesWithSetMongoGridFSFile() + { + $mongoGridFSFile = $this->getMockMongoGridFSFile(); + $mongoGridFSFile->expects($this->once()) + ->method('getBytes') + ->will($this->returnValue('bytes')); + + $file = new GridFSFile(); + $file->setMongoGridFSFile($mongoGridFSFile); + $this->assertEquals('bytes', $file->getBytes()); + } + + public function testGetBytesWithEmptyState() + { + $file = new GridFSFile(); + $this->assertNull($file->getBytes()); + } + + public function testWriteWithSetBytes() + { + $path = tempnam(sys_get_temp_dir(), 'doctrine_write_test'); + $this->assertNotEquals(false, $path); + + $file = new GridFSFile(); + $file->setBytes('bytes'); + $file->write($path); + $this->assertStringEqualsFile($path, 'bytes'); + unlink($path); + } + + public function testWriteWithSetFilename() + { + $path = tempnam(sys_get_temp_dir(), 'doctrine_write_test'); + $this->assertNotEquals(false, $path); + + $file = new GridFSFile(); + $file->setFilename(__FILE__); + $file->write($path); + $this->assertFileEquals(__FILE__, $path); + unlink($path); + } + + public function testWriteWithSetMongoGridFSFile() + { + $mongoGridFSFile = $this->getMockMongoGridFSFile(); + $mongoGridFSFile->expects($this->once()) + ->method('write') + ->with('filename'); + + $file = new GridFSFile(); + $file->setMongoGridFSFile($mongoGridFSFile); + $file->write('filename'); + } + + /** + * @expectedException BadMethodCallException + */ + public function testWriteWithEmptyState() + { + $file = new GridFSFile(); + $file->write('filename'); + } + + public function testGetSizeWithSetBytes() + { + $file = new GridFSFile(); + $file->setBytes('bytes'); + $this->assertEquals(5, $file->getSize()); + } + + public function testGetSizeWithSetFilename() + { + $file = new GridFSFile(); + $file->setFilename(__FILE__); + $this->assertEquals(filesize(__FILE__), $file->getSize()); + } + + public function testGetSizeWithSetMongoGridFSFile() + { + $mongoGridFSFile = $this->getMockMongoGridFSFile(); + $mongoGridFSFile->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(200)); + + $file = new GridFSFile(); + $file->setMongoGridFSFile($mongoGridFSFile); + $this->assertEquals(200, $file->getSize()); + } + + public function testGetSizeWithEmptyState() + { + $file = new GridFSFile(); + $this->assertEquals(0, $file->getSize()); + } + + private function getMockMongoGridFSFile() + { + return $this->getMockBuilder('MongoGridFSFile') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSTest.php new file mode 100644 index 00000000..b89c6ad8 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/GridFSTest.php @@ -0,0 +1,164 @@ + 'bar', + 'file' => new GridFSFile(__FILE__), + ); + + $gridFS = $this->getGridFS(); + $gridFS->insert($document); + + $document = $gridFS->findOne(array('_id' => $document['_id'])); + + $this->assertTrue(isset($document['_id'])); + $this->assertEquals('bar', $document['foo']); + + $file = $document['file']; + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $file); + $this->assertFalse($file->isDirty()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertStringEqualsFile(__FILE__, $file->getBytes()); + $this->assertEquals(filesize(__FILE__), $file->getSize()); + + $path = tempnam(sys_get_temp_dir(), 'doctrine_write_test'); + $this->assertNotEquals(false, $path); + + $file->write($path); + $this->assertFileEquals(__FILE__, $path); + unlink($path); + } + + public function testStoreFile() + { + $document = array('foo' => 'bar'); + + $gridFS = $this->getGridFS(); + $file = $gridFS->storeFile(__FILE__, $document); + + $this->assertTrue(isset($document['_id'])); + $this->assertEquals('bar', $document['foo']); + + $this->assertInstanceOf('Doctrine\MongoDB\GridFSFile', $file); + $this->assertFalse($file->isDirty()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertStringEqualsFile(__FILE__, $file->getBytes()); + $this->assertEquals(filesize(__FILE__), $file->getSize()); + } + + public function testUpdate() + { + $gridFS = $this->getGridFS(); + + $path = __DIR__.'/file.txt'; + $file = new GridFSFile($path); + $document = array( + 'title' => 'Test Title', + 'file' => $file + ); + $gridFS->insert($document); + $id = $document['_id']; + + $document = $gridFS->findOne(array('_id' => $id)); + $file = $document['file']; + + $gridFS->update(array('_id' => $id), array('$pushAll' => array('test' => array(1, 2, 3)))); + $check = $gridFS->findOne(array('_id' => $id)); + $this->assertTrue(isset($check['test'])); + $this->assertEquals(3, count($check['test'])); + $this->assertEquals(array(1, 2, 3), $check['test']); + + $gridFS->update(array('_id' => $id), array('_id' => $id)); + $gridFS->update(array('_id' => $id), array('_id' => $id, 'boom' => true)); + $check = $gridFS->findOne(array('_id' => $id)); + $this->assertFalse(array_key_exists('test', $check)); + $this->assertTrue($check['boom']); + } + + public function testUpsertDocumentWithoutFile() + { + $gridFS = $this->getGridFS(); + + $gridFS->update( + array('id' => 123), + array('x' => 1), + array('upsert' => true, 'multiple' => false) + ); + + $document = $gridFS->findOne(); + + $this->assertNotNull($document); + $this->assertNotEquals(123, $document['_id']); + $this->assertEquals(1, $document['x']); + } + + public function testUpsertDocumentWithoutFileWithId() + { + $gridFS = $this->getGridFS(); + + $gridFS->update( + array('x' => 1), + array('_id' => 123), + array('upsert' => true, 'multiple' => false) + ); + + $document = $gridFS->findOne(array('_id' => 123)); + + $this->assertNotNull($document); + $this->assertFalse(array_key_exists('x', $document)); + } + + public function testUpsertModifierWithoutFile() + { + $gridFS = $this->getGridFS(); + + $gridFS->update( + array('_id' => 123), + array('$set' => array('x' => 1)), + array('upsert' => true, 'multiple' => false) + ); + + $document = $gridFS->findOne(array('_id' => 123)); + + $this->assertNotNull($document); + $this->assertEquals(1, $document['x']); + } + + public function testUpsert() + { + $gridFS = $this->getGridFS(); + $id = new \MongoId(); + + $path = __DIR__.'/file.txt'; + $file = new GridFSFile($path); + + $newObj = array( + '$set' => array( + 'title' => 'Test Title', + 'file' => $file, + ), + ); + $gridFS->update(array('_id' => $id), $newObj, array('upsert' => true, 'multiple' => false)); + + $document = $gridFS->findOne(array('_id' => $id)); + + $file = $document['file']; + + $this->assertFalse($file->isDirty()); + $this->assertEquals($path, $file->getFilename()); + $this->assertEquals(file_get_contents($path), $file->getBytes()); + $this->assertEquals(22, $file->getSize()); + } + + private function getGridFS() + { + return $this->conn->selectDatabase(self::$dbName)->getGridFS(); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/LoggableCursorTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/LoggableCursorTest.php new file mode 100644 index 00000000..766a5681 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/LoggableCursorTest.php @@ -0,0 +1,28 @@ +conn->selectCollection('foo', 'bar')->find(); + $cursor->$method($argument); + } + + public function provideLoggedMethods() + { + return array( + array('sort', array()), + array('skip'), + array('limit'), + array('hint', array()), + array('snapshot'), + ); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php new file mode 100644 index 00000000..d3b3a8e0 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php @@ -0,0 +1,529 @@ +getTestQueryBuilder() + ->distinct('count') + ->field('username')->equals('distinct_test'); + + $expected = array( + 'username' => 'distinct_test' + ); + $query = $qb->getQuery(); + $this->assertInstanceOf('Doctrine\MongoDB\Query\Query', $query); + $this->assertEquals(Query::TYPE_DISTINCT_FIELD, $query->getType()); + $this->assertEquals($expected, $qb->getQueryArray()); + $this->assertInstanceof('Doctrine\MongoDB\ArrayIterator', $query->execute()); + } + + public function testFindAndRemoveQuery() + { + $qb = $this->getTestQueryBuilder() + ->findAndRemove() + ->field('username')->equals('jwage'); + + $this->assertEquals(Query::TYPE_FIND_AND_REMOVE, $qb->getType()); + $expected = array( + 'username' => 'jwage' + ); + $this->assertEquals($expected, $qb->getQueryArray()); + $this->assertNull($qb->getQuery()->execute()); + } + + public function testMapReduceQueryWithSingleMethod() + { + $map = 'function() { + for(i = 0; i <= this.options.length; i++) { + emit(this.name, { count: 1 }); + } + }'; + + $reduce = 'function(product, values) { + var total = 0 + values.forEach(function(value){ + total+= value.count; + }); + return { + product: product, + options: total, + test: values + }; + }'; + + $finalize = 'function (key, value) { return value; }'; + + $out = array('inline' => true); + + $qb = $this->getTestQueryBuilder() + ->mapReduce($map, $reduce, $out, array('finalize' => $finalize)); + + $expectedMapReduce = array( + 'map' => $map, + 'reduce' => $reduce, + 'out' => array('inline' => true), + 'options' => array('finalize' => $finalize), + ); + + $this->assertEquals(Query::TYPE_MAP_REDUCE, $qb->getType()); + $this->assertEquals($expectedMapReduce, $qb->debug('mapReduce')); + } + + public function testMapReduceQueryWithMultipleMethodsAndQueryArray() + { + $map = 'function() { + for(i = 0; i <= this.options.length; i++) { + emit(this.name, { count: 1 }); + } + }'; + + $reduce = 'function(product, values) { + var total = 0 + values.forEach(function(value){ + total+= value.count; + }); + return { + product: product, + options: total, + test: values + }; + }'; + + $finalize = 'function (key, value) { return value; }'; + + $qb = $this->getTestQueryBuilder() + ->map($map) + ->reduce($reduce) + ->finalize($finalize) + ->field('username')->equals('jwage'); + + $expectedQueryArray = array('username' => 'jwage'); + $expectedMapReduce = array( + 'map' => $map, + 'reduce' => $reduce, + 'options' => array('finalize' => $finalize), + ); + + $this->assertEquals(Query::TYPE_MAP_REDUCE, $qb->getType()); + $this->assertEquals($expectedQueryArray, $qb->getQueryArray()); + $this->assertEquals($expectedMapReduce, $qb->debug('mapReduce')); + } + + public function testFindAndUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->findAndRemove() + ->field('username')->equals('jwage'); + + $this->assertEquals(Query::TYPE_FIND_AND_REMOVE, $qb->getType()); + $expected = array( + 'username' => 'jwage' + ); + $this->assertEquals($expected, $qb->getQueryArray()); + + $query = $qb->getQuery(); + $this->assertEquals(Query::TYPE_FIND_AND_REMOVE, $query->getType()); + $this->assertNull($query->execute()); + } + + public function testGroupQueryWithSingleMethod() + { + $keys = array(); + $initial = array('count' => 0, 'sum' => 0); + $reduce = 'function(obj, prev) { prev.count++; prev.sum += obj.a; }'; + $finalize = 'function(obj) { if (obj.count) { obj.avg = obj.sum / obj.count; } else { obj.avg = 0; } }'; + + $qb = $this->getTestQueryBuilder() + ->group($keys, $initial, $reduce, array('finalize' => $finalize)); + + $expected = array( + 'keys' => $keys, + 'initial' => $initial, + 'reduce' => $reduce, + 'options' => array('finalize' => $finalize), + ); + + $this->assertEquals(Query::TYPE_GROUP, $qb->getType()); + $this->assertEquals($expected, $qb->debug('group')); + $this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute()); + } + + public function testGroupQueryWithMultipleMethods() + { + $keys = array(); + $initial = array('count' => 0, 'sum' => 0); + $reduce = 'function(obj, prev) { prev.count++; prev.sum += obj.a; }'; + $finalize = 'function(obj) { if (obj.count) { obj.avg = obj.sum / obj.count; } else { obj.avg = 0; } }'; + + $qb = $this->getTestQueryBuilder() + ->group($keys, $initial) + ->reduce($reduce) + ->finalize($finalize); + + $expected = array( + 'keys' => $keys, + 'initial' => $initial, + 'reduce' => $reduce, + 'options' => array('finalize' => $finalize), + ); + + $this->assertEquals(Query::TYPE_GROUP, $qb->getType()); + $this->assertEquals($expected, $qb->debug('group')); + $this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute()); + } + + public function testInsertQuery() + { + $qb = $this->getTestQueryBuilder() + ->insert() + ->field('username')->set('jwage'); + + $expected = array( + 'username' => 'jwage' + ); + $this->assertEquals($expected, $qb->getNewObj()); + $this->assertEquals(Query::TYPE_INSERT, $qb->getType()); + $this->assertArrayHasKeyValue(array('ok' => 1.0), $qb->getQuery()->execute()); + } + + public function testUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->field('username')->set('jwage'); + + $expected = array( + '$set' => array( + 'username' => 'jwage' + ) + ); + $this->assertEquals($expected, $qb->getNewObj()); + $this->assertEquals(Query::TYPE_UPDATE, $qb->getType()); + + $query = $qb->getQuery(); + $this->assertEquals(Query::TYPE_UPDATE, $query->getType()); + $this->assertArrayHasKeyValue(array('ok' => 1.0), $query->execute()); + } + + public function testRemoveQuery() + { + $qb = $this->getTestQueryBuilder() + ->remove() + ->field('username')->equals('jwage'); + + $this->assertEquals(Query::TYPE_REMOVE, $qb->getType()); + $this->assertArrayHasKeyValue(array('ok' => 1.0), $qb->getQuery()->execute()); + } + + /** + * @expectedException BadMethodCallException + */ + public function testFinalizeShouldThrowExceptionForUnsupportedQueryType() + { + $qb = $this->getTestQueryBuilder()->finalize('function() { }'); + } + + /** + * @expectedException BadMethodCallException + */ + public function testReduceShouldThrowExceptionForUnsupportedQueryType() + { + $qb = $this->getTestQueryBuilder()->reduce('function() { }'); + } + + public function testThatOrAcceptsAnotherQuery() + { + $coll = $this->conn->selectCollection('db', 'users'); + + $qb = $coll->createQueryBuilder(); + $qb->addOr($qb->expr()->field('firstName')->equals('Kris')); + $qb->addOr($qb->expr()->field('firstName')->equals('Chris')); + + $this->assertEquals(array('$or' => array( + array('firstName' => 'Kris'), + array('firstName' => 'Chris') + )), $qb->getQueryArray()); + } + + public function testThatAndAcceptsAnotherQuery() + { + $coll = $this->conn->selectCollection('db', 'users'); + + $qb = $coll->createQueryBuilder(); + $qb->addAnd($qb->expr()->field('hits')->gte(1)); + $qb->addAnd($qb->expr()->field('hits')->lt(5)); + + $this->assertEquals(array( + '$and' => array( + array('hits' => array('$gte' => 1)), + array('hits' => array('$lt' => 5)), + ), + ), $qb->getQueryArray()); + } + + public function testAddElemMatch() + { + $qb = $this->getTestQueryBuilder(); + $qb->field('phonenumbers')->elemMatch($qb->expr()->field('phonenumber')->equals('6155139185')); + $expected = array('phonenumbers' => array( + '$elemMatch' => array('phonenumber' => '6155139185') + )); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testAddNot() + { + $qb = $this->getTestQueryBuilder(); + $qb->field('username')->not($qb->expr()->in(array('boo'))); + $expected = array( + 'username' => array( + '$not' => array( + '$in' => array('boo') + ) + ) + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testFindQuery() + { + $qb = $this->getTestQueryBuilder() + ->where("function() { return this.username == 'boo' }"); + $expected = array( + '$where' => "function() { return this.username == 'boo' }" + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testUpsertUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->upsert(true) + ->field('username')->set('jwage'); + + $expected = array( + '$set' => array( + 'username' => 'jwage' + ) + ); + $this->assertEquals($expected, $qb->getNewObj()); + $this->assertTrue($qb->debug('upsert')); + } + + public function testMultipleUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->multiple(true) + ->field('username')->set('jwage'); + + $expected = array( + '$set' => array( + 'username' => 'jwage' + ) + ); + $this->assertEquals($expected, $qb->getNewObj()); + $this->assertTrue($qb->debug('multiple')); + } + + public function testComplexUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->field('username') + ->set('jwage') + ->equals('boo'); + + $expected = array( + 'username' => 'boo' + ); + $this->assertEquals($expected, $qb->getQueryArray()); + + $expected = array('$set' => array( + 'username' => 'jwage' + )); + $this->assertEquals($expected, $qb->getNewObj()); + } + + public function testIncUpdateQuery() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->field('hits')->inc(5) + ->field('username')->equals('boo'); + + $expected = array( + 'username' => 'boo' + ); + $this->assertEquals($expected, $qb->getQueryArray()); + + $expected = array('$inc' => array( + 'hits' => 5 + )); + $this->assertEquals($expected, $qb->getNewObj()); + } + + public function testUnsetField() + { + $qb = $this->getTestQueryBuilder() + ->update() + ->field('hits')->unsetField() + ->field('username')->equals('boo'); + + $expected = array( + 'username' => 'boo' + ); + $this->assertEquals($expected, $qb->getQueryArray()); + + $expected = array('$unset' => array( + 'hits' => 1 + )); + $this->assertEquals($expected, $qb->getNewObj()); + } + + public function testDateRange() + { + $start = new \MongoDate(strtotime('1985-09-01 01:00:00')); + $end = new \MongoDate(strtotime('1985-09-04')); + $qb = $this->getTestQueryBuilder(); + $qb->field('createdAt')->range($start, $end); + + $expected = array( + 'createdAt' => array( + '$gte' => $start, + '$lt' => $end + ) + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testQueryIsIterable() + { + $qb = $this->getTestQueryBuilder(); + $query = $qb->getQuery(); + $this->assertInstanceOf('IteratorAggregate', $query); + $this->assertInstanceOf('Doctrine\MongoDB\IteratorAggregate', $query); + } + + public function testDeepClone() + { + $qb = $this->getTestQueryBuilder(); + + $qb->field('username')->equals('jwage'); + + $this->assertCount(1, $qb->getQueryArray()); + + $qb2 = clone $qb; + $qb2->field('firstName')->equals('Jon'); + + $this->assertCount(1, $qb->getQueryArray()); + } + + public function testGeoNearQuery() + { + $qb = $this->getTestQueryBuilder() + ->geoNear(50, 50) + ->distanceMultiplier(2.5) + ->maxDistance(5) + ->spherical(true) + ->field('type')->equals('restaurant') + ->limit(10); + + $this->assertEquals(Query::TYPE_GEO_LOCATION, $qb->getType()); + + $expectedQuery = array('type' => 'restaurant'); + $this->assertEquals($expectedQuery, $qb->getQueryArray()); + + $geoNear = $qb->debug('geoNear'); + $this->assertEquals(array(50, 50), $geoNear['near']); + $this->assertEquals(2.5, $geoNear['distanceMultiplier']); + $this->assertEquals(5, $geoNear['maxDistance']); + $this->assertEquals(true, $geoNear['spherical']); + $this->assertEquals(10, $qb->debug('limit')); + $this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute()); + } + + public function testNear() + { + $qb = $this->getTestQueryBuilder() + ->field('loc')->near(50, 50)->maxDistance(25); + + $expected = array('loc' => array('$near' => array(50, 50), '$maxDistance' => 25)); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testWithinBox() + { + $qb = $this->getTestQueryBuilder() + ->field('loc')->withinBox(0, 0, 2, 2); + + $expected = array( + 'loc' => array( + '$within' => array( + '$box' => array(array(0, 0), array(2, 2)), + ), + ), + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testWithinCenter() + { + $qb = $this->getTestQueryBuilder() + ->field('loc')->withinCenter(0, 0, 1); + + $expected = array( + 'loc' => array( + '$within' => array( + '$center' => array(array(0, 0), 1), + ), + ), + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + public function testWithinPolygon() + { + $qb = $this->getTestQueryBuilder() + ->field('loc')->withinPolygon(array(0, 0), array(2, 0), array(0, 2)); + + $expected = array( + 'loc' => array( + '$within' => array( + '$polygon' => array(array(0, 0), array(2, 0), array(0, 2)), + ), + ), + ); + $this->assertEquals($expected, $qb->getQueryArray()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testWithinPolygonRequiresAtLeastThreePoints() + { + $qb = $this->getTestQueryBuilder() + ->field('loc')->withinPolygon(array(0, 0), array(1, 1)); + } + + private function getTestQueryBuilder() + { + return $this->conn->selectCollection('db', 'users')->createQueryBuilder(); + } + + private function assertArrayHasKeyValue($expected, $array, $message = '') + { + foreach ((array) $expected as $key => $value) { + $this->assertArrayHasKey($key, $expected, $message); + $this->assertEquals($value, $expected[$key], $message); + } + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/QueryTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/QueryTest.php new file mode 100644 index 00000000..5ee4de34 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Query/QueryTest.php @@ -0,0 +1,101 @@ +getMockCollection(); + + $queryArray = array( + 'type' => Query::TYPE_MAP_REDUCE, + 'mapReduce' => array( + 'map' => '', + 'reduce' => '', + 'options' => array('limit' => 10), + ), + 'query' => array() + ); + + $query = new Query( + $this->getMockDatabase(), + $collection, + $queryArray, + array(), + '' + ); + + $collection->expects($this->any()) + ->method('mapReduce') + ->with($this->anything(), + $this->anything(), + $this->anything(), + $this->anything(), + new ArrayHasValueUnderKey('limit', 10) + ); + + $query->execute(); + } + + public function testGeoNearOptionsArePassed() + { + $collection = $this->getMockCollection(); + + $queryArray = array( + 'type' => Query::TYPE_GEO_LOCATION, + 'geoNear' => array( + 'near' => array(50, 50), + 'distanceMultiplier' => 2.5, + 'maxDistance' => 5, + 'spherical' => true, + ), + 'limit' => 10, + 'query' => array('altitude' => array('$gt' => 1)), + ); + + $query = new Query( + $this->getMockDatabase(), + $collection, + $queryArray, + array(), + '' + ); + + $collection->expects($this->any()) + ->method('geoNear') + ->with(array(50, 50), + array('altitude' => array('$gt' => 1)), + $this->logicalAnd( + new ArrayHasValueUnderKey('distanceMultiplier', 2.5), + new ArrayHasValueUnderKey('maxDistance', 5), + new ArrayHasValueUnderKey('spherical', true), + new ArrayHasValueUnderKey('num', 10) + ) + ); + + $query->execute(); + } + + /** + * @return \Doctrine\MongoDB\Collection + */ + private function getMockCollection() + { + return $this->getMockBuilder('Doctrine\MongoDB\Collection') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return \Doctrine\MongoDB\Database + */ + private function getMockDatabase() + { + return $this->getMockBuilder('Doctrine\MongoDB\Database') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/RetryTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/RetryTest.php new file mode 100644 index 00000000..ea26d205 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/RetryTest.php @@ -0,0 +1,165 @@ +setRetryConnect(1); + $config->setRetryQuery(1); + $this->conn = new Connection(null, array(), $config); + } + + public function testFunctional() + { + $test = $this->conn->selectDatabase('test')->selectCollection('test'); + $doc = array('test' => 'test'); + $test->insert($doc); + $check = $test->findOne(array('test' => 'test')); + $this->assertEquals('test', $check['test']); + $check = $test->find(array('test' => 'test')); + $this->assertInstanceOf('Doctrine\MongoDB\Cursor', $check); + $array = $check->toArray(); + $this->assertTrue(is_array($array)); + $array = array_values($array); + $this->assertEquals('test', $array[0]['test']); + } + + public function testCollectionRetries() + { + $database = $this->conn->selectDatabase('test'); + $mongoCollection = $database->selectCollection('test')->getMongoCollection(); + $collection = new CollectionStub($this->conn, $mongoCollection, $database, $this->conn->getEventManager(), '$', 1); + $exception = new \MongoException('Test'); + try { + $collection->testRetries($exception); + $this->fail(); + } catch (\Exception $e) { + } + $this->assertSame($e, $exception); + $this->assertEquals(2, $collection->numTimesTried); + } + + public function testConnectionRetries() + { + $config = new Configuration(); + $config->setRetryConnect(1); + $config->setRetryQuery(1); + $conn = new ConnectionStub(null, array(), $config); + $exception = new \MongoException('Test'); + try { + $conn->testRetries($exception); + $this->fail(); + } catch (\Exception $e) { + } + $this->assertSame($e, $exception); + $this->assertEquals(2, $conn->numTimesTried); + } + + public function testCursorCursorExceptionRetries() + { + $collection = $this->conn->selectDatabase('test')->selectCollection('test'); + $mongoCursor = $collection->find()->getMongoCursor(); + $cursor = new CursorStub($this->conn, $collection, $mongoCursor, array(), array(), 1); + $exception = new \MongoCursorException('Test'); + try { + $cursor->testCursorExceptionRetries($exception); + $this->fail(); + } catch (\Exception $e) { + } + $this->assertSame($e, $exception); + $this->assertEquals(2, $cursor->numTimesTried); + } + + public function testCursorConnectionExceptionRetries() + { + $collection = $this->conn->selectDatabase('test')->selectCollection('test'); + $mongoCursor = $collection->find()->getMongoCursor(); + $cursor = new CursorStub($this->conn, $collection, $mongoCursor, array(), array(), 1); + $exception = new \MongoConnectionException('Test'); + try { + $cursor->testConnectionExceptionRetries($exception); + $this->fail(); + } catch (\Exception $e) { + } + $this->assertSame($e, $exception); + $this->assertEquals(2, $cursor->numTimesTried); + } +} + +class CollectionStub extends Collection +{ + public $numTimesTried = 0; + + public function testRetries($exception) + { + $numTimesTried = 0; + try { + $this->retry(function() use(&$numTimesTried, $exception) { + $numTimesTried++; + throw $exception; + }); + } catch (\MongoException $e) {} + $this->numTimesTried = $numTimesTried; + throw $e; + } +} + +class ConnectionStub extends Connection +{ + public $numTimesTried = 0; + + public function testRetries($exception) + { + $numTimesTried = 0; + try { + $this->retry(function() use(&$numTimesTried, $exception) { + $numTimesTried++; + throw $exception; + }); + } catch (\MongoException $e) {} + $this->numTimesTried = $numTimesTried; + throw $e; + } +} + +class CursorStub extends Cursor +{ + public $numTimesTried = 0; + + public function testCursorExceptionRetries($exception) + { + $this->numTimesTried = 0; + $numTimesTried = 0; + try { + $this->retry(function() use(&$numTimesTried, $exception) { + $numTimesTried++; + throw $exception; + }, true); + } catch (\MongoCursorException $e) {} + $this->numTimesTried = $numTimesTried; + throw $e; + } + + public function testConnectionExceptionRetries($exception) + { + $this->numTimesTried = 0; + $numTimesTried = 0; + try { + $this->retry(function() use(&$numTimesTried, $exception) { + $numTimesTried++; + throw $exception; + }, true); + } catch (\MongoConnectionException $e) {} + $this->numTimesTried = $numTimesTried; + throw $e; + } +} \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Util/ReadPreferenceTest.php b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Util/ReadPreferenceTest.php new file mode 100644 index 00000000..1088648e --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/Util/ReadPreferenceTest.php @@ -0,0 +1,98 @@ +markTestSkipped('This test is not applicable to driver versions < 1.3.0'); + } + } + + /** + * @expectedException InvalidArgumentException + */ + public function testConvertNumericTypeShouldThrowExceptionForInvalidType() + { + ReadPreference::convertNumericType(-1); + } + + /** + * @dataProvider provideTagSets + */ + public function testConvertTagSets($tagSet, $expected) + { + $this->assertEquals($expected, ReadPreference::convertTagSets($tagSet)); + } + + public function provideTagSets() + { + return array( + array( + array( + array('dc:east', 'use:reporting'), + array('dc:west'), + array(), + ), + array( + array('dc' => 'east', 'use' => 'reporting'), + array('dc' => 'west'), + array(), + ), + ), + array( + array(array()), + array(array()), + ), + /* This tag set is impractical, since an empty set matches anything, + * but we want to test that elements beyond the first are converted. + */ + array( + array( + array(), + array('dc:west'), + array('dc:east', 'use:reporting'), + ), + array( + array(), + array('dc' => 'west'), + array('dc' => 'east', 'use' => 'reporting'), + ), + ), + ); + } + + /** + * @dataProvider provideTagSetsAcceptedBySetReadPreference + */ + public function testConvertTagSetsShouldNotAlterTagSetsAcceptedBySetReadPreference($tagSet) + { + $this->assertEquals($tagSet, ReadPreference::convertTagSets($tagSet)); + } + + public function provideTagSetsAcceptedBySetReadPreference() + { + return array( + array( + array( + array('dc' => 'east', 'use' => 'reporting'), + array('dc' => 'west'), + array(), + ), + ), + /* These numeric tag names are likely impractical, but they should + * be accepted by setReadPreference() and thus not modified. + */ + array( + array( + array('0' => 'zero', '1' => 'one'), + array(), + ), + ), + ); + } +} diff --git a/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/file.txt b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/file.txt new file mode 100644 index 00000000..b7801914 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/Doctrine/MongoDB/Tests/file.txt @@ -0,0 +1 @@ +These are the bytes... \ No newline at end of file diff --git a/vendor/doctrine/mongodb/tests/bootstrap.php b/vendor/doctrine/mongodb/tests/bootstrap.php new file mode 100644 index 00000000..9be11ae3 --- /dev/null +++ b/vendor/doctrine/mongodb/tests/bootstrap.php @@ -0,0 +1,9 @@ +add('Doctrine\MongoDB\Tests', __DIR__ . '/../tests'); diff --git a/vendor/doctrine/orm/LICENSE b/vendor/doctrine/orm/LICENSE new file mode 100644 index 00000000..4a91f0bf --- /dev/null +++ b/vendor/doctrine/orm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 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/orm/README.markdown b/vendor/doctrine/orm/README.markdown new file mode 100644 index 00000000..64f7189a --- /dev/null +++ b/vendor/doctrine/orm/README.markdown @@ -0,0 +1,20 @@ +# Doctrine 2 ORM + +Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2) +2.3: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.3)](http://travis-ci.org/doctrine/doctrine2) +2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2) +2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2) + +Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence +for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features +is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), +inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility +without requiring unnecessary code duplication. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC) +* [Downloads](http://github.com/doctrine/doctrine2/downloads) + diff --git a/vendor/doctrine/orm/UPGRADE.md b/vendor/doctrine/orm/UPGRADE.md new file mode 100644 index 00000000..b864d861 --- /dev/null +++ b/vendor/doctrine/orm/UPGRADE.md @@ -0,0 +1,525 @@ +# Upgrade to 2.4 + +## BC BREAK: Compatibility Bugfix in PersistentCollection#matching() + +In Doctrine 2.3 it was possible to use the new ``matching($criteria)`` +functionality by adding constraints for assocations based on ID: + + Criteria::expr()->eq('association', $assocation->getId()); + +This functionality does not work on InMemory collections however, because +in memory criteria compares object values based on reference. +As of 2.4 the above code will throw an exception. You need to change +offending code to pass the ``$assocation`` reference directly: + + Criteria::expr()->eq('association', $assocation); + +## Composer is now the default autoloader + +The test suite now runs with composer autoloading. Support for PEAR, and tarball autoloading is deprecated. +Support for GIT submodules is removed. + +## OnFlush and PostFlush event always called + +Before 2.4 the postFlush and onFlush events were only called when there were +actually entities that changed. Now these events are called no matter if there +are entities in the UoW or changes are found. + +## Parenthesis are now considered in arithmetic expression + +Before 2.4 parenthesis are not considered in arithmetic primary expression. +That's conceptually wrong, since it might result in wrong values. For example: + +The DQL: + + SELECT 100 / ( 2 * 2 ) FROM MyEntity + +Before 2.4 it generates the SQL: + + SELECT 100 / 2 * 2 FROM my_entity + +Now parenthesis are considered, the previous DQL will generate: + + SELECT 100 / (2 * 2) FROM my_entity + +# Upgrade to 2.3 + +## EntityManager#find() not calls EntityRepository#find() anymore + +Previous to 2.3, calling ``EntityManager#find()`` would be delegated to +``EntityRepository#find()``. This has lead to some unexpected behavior in the +core of Doctrine when people have overwritten the find method in their +repositories. That is why this behavior has been reversed in 2.3, and +``EntityRepository#find()`` calls ``EntityManager#find()`` instead. + +## EntityGenerator add*() method generation + +When generating an add*() method for a collection the EntityGenerator will now not +use the Type-Hint to get the singular for the collection name, but use the field-name +and strip a trailing "s" character if there is one. + +## Merge copies non persisted properties too + +When merging an entity in UoW not only mapped properties are copied, but also others. + +## Query, QueryBuilder and NativeQuery parameters *BC break* + +From now on, parameters in queries is an ArrayCollection instead of a simple array. +This affects heavily the usage of setParameters(), because it will not append anymore +parameters to query, but will actually override the already defined ones. +Whenever you are retrieving a parameter (ie. $query->getParameter(1)), you will +receive an instance of Query\Parameter, which contains the methods "getName", +"getValue" and "getType". Parameters are also only converted to when necessary, and +not when they are set. + +Also, related functions were affected: + +* execute($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* iterate($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* setParameters($parameters) the argument $parameters can be either an key=>value array or an ArrayCollection instance +* getParameters() now returns ArrayCollection instead of array +* getParameter($key) now returns Parameter instance instead of parameter value + +## Query TreeWalker method renamed + +Internal changes were made to DQL and SQL generation. If you have implemented your own TreeWalker, +you probably need to update it. The method walkJoinVariableDeclaration is now named walkJoin. + +## New methods in TreeWalker interface *BC break* + +Two methods getQueryComponents() and setQueryComponent() were added to the TreeWalker interface and all its implementations +including TreeWalkerAdapter, TreeWalkerChain and SqlWalker. If you have your own implementation not inheriting from one of the +above you must implement these new methods. + +## Metadata Drivers + +Metadata drivers have been rewritten to reuse code from Doctrine\Common. Anyone who is using the +`Doctrine\ORM\Mapping\Driver\Driver` interface should instead refer to +`Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`. Same applies to +`Doctrine\ORM\Mapping\Driver\AbstractFileDriver`: you should now refer to +`Doctrine\Common\Persistence\Mapping\Driver\FileDriver`. + +Also, following mapping drivers have been deprecated, please use their replacements in Doctrine\Common as listed: + + * `Doctrine\ORM\Mapping\Driver\DriverChain` => `Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain` + * `Doctrine\ORM\Mapping\Driver\PHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\PHPDriver` + * `Doctrine\ORM\Mapping\Driver\StaticPHPDriver` => `Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver` + +# Upgrade to 2.2 + +## ResultCache implementation rewritten + +The result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery +anymore. This means that for result cached queries the hydration will now always be performed again, regardless of +the hydration mode. Affected areas are: + +1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork + leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore. +2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result. + +The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now +deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile` +instance with access to result cache driver, lifetime and cache key. + + +## EntityManager#getPartialReference() creates read-only entity + +Entities returned from EntityManager#getPartialReference() are now marked as read-only if they +haven't been in the identity map before. This means objects of this kind never lead to changes +in the UnitOfWork. + + +## Fields omitted in a partial DQL query or a native query are never updated + +Fields of an entity that are not returned from a partial DQL Query or native SQL query +will never be updated through an UPDATE statement. + + +## Removed support for onUpdate in @JoinColumn + +The onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed. + + +## Changes in Annotation Handling + +There have been some changes to the annotation handling in Common 2.2 again, that affect how people with old configurations +from 2.0 have to configure the annotation driver if they don't use `Configuration::newDefaultAnnotationDriver()`: + + // Register the ORM Annotations in the AnnotationRegistry + AnnotationRegistry::registerFile('path/to/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'); + + $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache()); + + $driver = new AnnotationDriver($reader, (array)$paths); + + $config->setMetadataDriverImpl($driver); + + +## Scalar mappings can now be omitted from DQL result + +You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore. +Example: + +SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10 + +Your result will be a collection of Users, and not an array with key 0 as User object instance and "numArticles" as the number of articles per user + + +## Map entities as scalars in DQL result + +When hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance. +You are now allowed to alias this, providing more flexibility for you code. +Example: + +SELECT u AS user FROM User u + +Will now return a collection of arrays with index "user" pointing to the User object instance. + + +## Performance optimizations + +Thousands of lines were completely reviewed and optimized for best performance. +Removed redundancy and improved code readability made now internal Doctrine code easier to understand. +Also, Doctrine 2.2 now is around 10-15% faster than 2.1. + +## EntityManager#find(null) + +Previously EntityManager#find(null) returned null. It now throws an exception. + +# Upgrade to 2.1 + +## Interface for EntityRepository + +The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepository. This means that your classes that override EntityRepository and extend find(), findOneBy() or findBy() must be adjusted to follow this interface. + +## 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: + + // new call to the AnnotationRegistry + \Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'); + + $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() + ); + +This is already done inside the ``$config->newDefaultAnnotationDriver``, so everything should automatically work if you are using this method. You can verify if everything still works by executing a console command such as schema-validate that loads all metadata into memory. + +# Update from 2.0-BETA3 to 2.0-BETA4 + +## XML Driver element demoted to attribute + +We changed how the XML Driver allows to define the change-tracking-policy. The working case is now: + + + +# Update from 2.0-BETA2 to 2.0-BETA3 + +## Serialization of Uninitialized Proxies + +As of Beta3 you can now serialize uninitialized proxies, an exception will only be thrown when +trying to access methods on the unserialized proxy as long as it has not been re-attached to the +EntityManager using `EntityManager#merge()`. See this example: + + $proxy = $em->getReference('User', 1); + + $serializedProxy = serialize($proxy); + $detachedProxy = unserialized($serializedProxy); + + echo $em->contains($detachedProxy); // FALSE + + try { + $detachedProxy->getId(); // uninitialized detached proxy + } catch(Exception $e) { + + } + $attachedProxy = $em->merge($detachedProxy); + echo $attackedProxy->getId(); // works! + +## Changed SQL implementation of Postgres and Oracle DateTime types + +The DBAL Type "datetime" included the Timezone Offset in both Postgres and Oracle. As of this version they are now +generated without Timezone (TIMESTAMP WITHOUT TIME ZONE instead of TIMESTAMP WITH TIME ZONE). +See [this comment to Ticket DBAL-22](http://www.doctrine-project.org/jira/browse/DBAL-22?focusedCommentId=13396&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_13396) +for more details as well as migration issues for PostgreSQL and Oracle. + +Both Postgres and Oracle will throw Exceptions during hydration of Objects with "DateTime" fields unless migration steps are taken! + +## Removed multi-dot/deep-path expressions in DQL + +The support for implicit joins in DQL through the multi-dot/Deep Path Expressions +was dropped. For example: + + SELECT u FROM User u WHERE u.group.name = ?1 + +See the "u.group.id" here is using multi dots (deep expression) to walk +through the graph of objects and properties. Internally the DQL parser +would rewrite these queries to: + + SELECT u FROM User u JOIN u.group g WHERE g.name = ?1 + +This explicit notation will be the only supported notation as of now. The internal +handling of multi-dots in the DQL Parser was very complex, error prone in edge cases +and required special treatment for several features we added. Additionally +it had edge cases that could not be solved without making the DQL Parser +even much more complex. For this reason we will drop the support for the +deep path expressions to increase maintainability and overall performance +of the DQL parsing process. This will benefit any DQL query being parsed, +even those not using deep path expressions. + +Note that the generated SQL of both notations is exactly the same! You +don't loose anything through this. + +## Default Allocation Size for Sequences + +The default allocation size for sequences has been changed from 10 to 1. This step was made +to not cause confusion with users and also because it is partly some kind of premature optimization. + +# Update from 2.0-BETA1 to 2.0-BETA2 + +There are no backwards incompatible changes in this release. + +# Upgrade from 2.0-ALPHA4 to 2.0-BETA1 + +## EntityRepository deprecates access to protected variables + +Instead of accessing protected variables for the EntityManager in +a custom EntityRepository it is now required to use the getter methods +for all the three instance variables: + +* `$this->_em` now accessible through `$this->getEntityManager()` +* `$this->_class` now accessible through `$this->getClassMetadata()` +* `$this->_entityName` now accessible through `$this->getEntityName()` + +Important: For Beta 2 the protected visibility of these three properties will be +changed to private! + +## Console migrated to Symfony Console + +The Doctrine CLI has been replaced by Symfony Console Configuration + +Instead of having to specify: + + [php] + $cliConfig = new CliConfiguration(); + $cliConfig->setAttribute('em', $entityManager); + +You now have to configure the script like: + + [php] + $helperSet = new \Symfony\Components\Console\Helper\HelperSet(array( + 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + +## Console: No need for Mapping Paths anymore + +In previous versions you had to specify the --from and --from-path options +to show where your mapping paths are from the console. However this information +is already known from the Mapping Driver configuration, so the requirement +for this options were dropped. + +Instead for each console command all the entities are loaded and to +restrict the operation to one or more sub-groups you can use the --filter flag. + +## AnnotationDriver is not a default mapping driver anymore + +In conjunction with the recent changes to Console we realized that the +annotations driver being a default metadata driver lead to lots of glue +code in the console components to detect where entities lie and how to load +them for batch updates like SchemaTool and other commands. However the +annotations driver being a default driver does not really help that much +anyways. + +Therefore we decided to break backwards compatibility in this issue and drop +the support for Annotations as Default Driver and require our users to +specify the driver explicitly (which allows us to ask for the path to all +entities). + +If you are using the annotations metadata driver as default driver, you +have to add the following lines to your bootstrap code: + + $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__."/Entities")); + $config->setMetadataDriverImpl($driverImpl); + +You have to specify the path to your entities as either string of a single +path or array of multiple paths +to your entities. This information will be used by all console commands to +access all entities. + +Xml and Yaml Drivers work as before! + + +## New inversedBy attribute + +It is now *mandatory* that the owning side of a bidirectional association specifies the +'inversedBy' attribute that points to the name of the field on the inverse side that completes +the association. Example: + + [php] + // BEFORE (ALPHA4 AND EARLIER) + class User + { + //... + /** @OneToOne(targetEntity="Address", mappedBy="user") */ + private $address; + //... + } + class Address + { + //... + /** @OneToOne(targetEntity="User") */ + private $user; + //... + } + + // SINCE BETA1 + // User class DOES NOT CHANGE + class Address + { + //... + /** @OneToOne(targetEntity="User", inversedBy="address") */ + private $user; + //... + } + +Thus, the inversedBy attribute is the counterpart to the mappedBy attribute. This change +was necessary to enable some simplifications and further performance improvements. We +apologize for the inconvenience. + +## Default Property for Field Mappings + +The "default" option for database column defaults has been removed. If desired, database column defaults can +be implemented by using the columnDefinition attribute of the @Column annotation (or the appropriate XML and YAML equivalents). +Prefer PHP default values, if possible. + +## Selecting Partial Objects + +Querying for partial objects now has a new syntax. The old syntax to query for partial objects +now has a different meaning. This is best illustrated by an example. If you previously +had a DQL query like this: + + [sql] + SELECT u.id, u.name FROM User u + +Since BETA1, simple state field path expressions in the select clause are used to select +object fields as plain scalar values (something that was not possible before). +To achieve the same result as previously (that is, a partial object with only id and name populated) +you need to use the following, explicit syntax: + + [sql] + SELECT PARTIAL u.{id,name} FROM User u + +## XML Mapping Driver + +The 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e. +NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED + +## YAML Mapping Driver + +The way to specify lifecycle callbacks in YAML Mapping driver was changed to allow for multiple callbacks +per event. The Old syntax ways: + + [yaml] + lifecycleCallbacks: + doStuffOnPrePersist: prePersist + doStuffOnPostPersist: postPersist + +The new syntax is: + + [yaml] + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +## PreUpdate Event Listeners + +Event Listeners listening to the 'preUpdate' event can only affect the primitive values of entity changesets +by using the API on the `PreUpdateEventArgs` instance passed to the preUpdate listener method. Any changes +to the state of the entitys properties won't affect the database UPDATE statement anymore. This gives drastic +performance benefits for the preUpdate event. + +## Collection API + +The Collection interface in the Common package has been updated with some missing methods +that were present only on the default implementation, ArrayCollection. Custom collection +implementations need to be updated to adhere to the updated interface. + +# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4 + +## CLI Controller changes + +CLI main object changed its name and namespace. Renamed from Doctrine\ORM\Tools\Cli to Doctrine\Common\Cli\CliController. +Doctrine\Common\Cli\CliController now only deals with namespaces. Ready to go, Core, Dbal and Orm are available and you can subscribe new tasks by retrieving the namespace and including new task. Example: + + [php] + $cli->getNamespace('Core')->addTask('my-example', '\MyProject\Tools\Cli\Tasks\MyExampleTask'); + + +## CLI Tasks documentation + +Tasks have implemented a new way to build documentation. Although it is still possible to define the help manually by extending the basicHelp and extendedHelp, they are now optional. +With new required method AbstractTask::buildDocumentation, its implementation defines the TaskDocumentation instance (accessible through AbstractTask::getDocumentation()), basicHelp and extendedHelp are now not necessary to be implemented. + +## Changes in Method Signatures + + * A bunch of Methods on both Doctrine\DBAL\Platforms\AbstractPlatform and Doctrine\DBAL\Schema\AbstractSchemaManager + have changed quite significantly by adopting the new Schema instance objects. + +## Renamed Methods + + * Doctrine\ORM\AbstractQuery::setExpireResultCache() -> expireResultCache() + * Doctrine\ORM\Query::setExpireQueryCache() -> expireQueryCache() + +## SchemaTool Changes + + * "doctrine schema-tool --drop" now always drops the complete database instead of + only those tables defined by the current database model. The previous method had + problems when foreign keys of orphaned tables pointed to tables that were scheduled + for deletion. + * Use "doctrine schema-tool --update" to get a save incremental update for your + database schema without deleting any unused tables, sequences or foreign keys. + * Use "doctrine schema-tool --complete-update" to do a full incremental update of + your schema. +# Upgrade from 2.0-ALPHA2 to 2.0-ALPHA3 + +This section details the changes made to Doctrine 2.0-ALPHA3 to make it easier for you +to upgrade your projects to use this version. + +## CLI Changes + +The $args variable used in the cli-config.php for configuring the Doctrine CLI has been renamed to $globalArguments. + +## Proxy class changes + +You are now required to make supply some minimalist configuration with regards to proxy objects. That involves 2 new configuration options. First, the directory where generated proxy classes should be placed needs to be specified. Secondly, you need to configure the namespace used for proxy classes. The following snippet shows an example: + + [php] + // step 1: configure directory for proxy classes + // $config instanceof Doctrine\ORM\Configuration + $config->setProxyDir('/path/to/myproject/lib/MyProject/Generated/Proxies'); + $config->setProxyNamespace('MyProject\Generated\Proxies'); + +Note that proxy classes behave exactly like any other classes when it comes to class loading. Therefore you need to make sure the proxy classes can be loaded by some class loader. If you place the generated proxy classes in a namespace and directory under your projects class files, like in the example above, it would be sufficient to register the MyProject namespace on a class loader. Since the proxy classes are contained in that namespace and adhere to the standards for class loading, no additional work is required. +Generating the proxy classes into a namespace within your class library is the recommended setup. + +Entities with initialized proxy objects can now be serialized and unserialized properly from within the same application. + +For more details refer to the Configuration section of the manual. + +## Removed allowPartialObjects configuration option + +The allowPartialObjects configuration option together with the `Configuration#getAllowPartialObjects` and `Configuration#setAllowPartialObjects` methods have been removed. +The new behavior is as if the option were set to FALSE all the time, basically disallowing partial objects globally. However, you can still use the `Query::HINT_FORCE_PARTIAL_LOAD` query hint to force a query to return partial objects for optimization purposes. + +## Renamed Methods + +* Doctrine\ORM\Configuration#getCacheDir() to getProxyDir() +* Doctrine\ORM\Configuration#setCacheDir($dir) to setProxyDir($dir) diff --git a/vendor/doctrine/orm/bin/doctrine b/vendor/doctrine/orm/bin/doctrine new file mode 100644 index 00000000..c359ca7d --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine @@ -0,0 +1,4 @@ +#!/usr/bin/env php +. + */ + +require_once 'Doctrine/Common/ClassLoader.php'; + +$classLoader = new \Doctrine\Common\ClassLoader('Doctrine'); +$classLoader->register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); +$classLoader->register(); + +$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; + +$helperSet = null; +if (file_exists($configFile)) { + if ( ! is_readable($configFile)) { + trigger_error( + 'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR + ); + } + + require $configFile; + + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet); diff --git a/vendor/doctrine/orm/bin/doctrine.bat b/vendor/doctrine/orm/bin/doctrine.bat new file mode 100644 index 00000000..a9e8ceef --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.bat @@ -0,0 +1,9 @@ +@echo off + +if "%PHPBIN%" == "" set PHPBIN=@php_bin@ +if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH +GOTO RUN +:USE_PEAR_PATH +set PHPBIN=%PHP_PEAR_PHP_BIN% +:RUN +"%PHPBIN%" "@bin_dir@\doctrine" %* diff --git a/vendor/doctrine/orm/bin/doctrine.php b/vendor/doctrine/orm/bin/doctrine.php new file mode 100644 index 00000000..c7355628 --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.php @@ -0,0 +1,59 @@ +. + */ + +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Tools\Console\ConsoleRunner; + +(@include_once __DIR__ . '/../vendor/autoload.php') || @include_once __DIR__ . '/../../../autoload.php'; + +$directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'); + +$configFile = null; +foreach ($directories as $directory) { + $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; + + if (file_exists($configFile)) { + break; + } +} + +if ( ! file_exists($configFile)) { + ConsoleRunner::printCliConfigTemplate(); + exit(1); +} + +if ( ! is_readable($configFile)) { + echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n"; + exit(1); +} + +$commands = array(); + +$helperSet = require $configFile; + +if ( ! ($helperSet instanceof HelperSet)) { + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands); diff --git a/vendor/doctrine/orm/composer.json b/vendor/doctrine/orm/composer.json new file mode 100644 index 00000000..88236f2f --- /dev/null +++ b/vendor/doctrine/orm/composer.json @@ -0,0 +1,37 @@ +{ + "name": "doctrine/orm", + "type": "library","version":"2.4.0-RC1", + "description": "Object-Relational-Mapper for PHP", + "keywords": ["orm", "database"], + "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"} + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.3.2", + "ext-pdo": "*", + "doctrine/collections": "~1.1", + "doctrine/dbal": ">=2.4-beta,<2.5-dev", + "symfony/console": "2.*" + }, + "require-dev": { + "symfony/yaml": "2.1" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "autoload": { + "psr-0": { "Doctrine\\ORM\\": "lib/" } + }, + "bin": ["bin/doctrine", "bin/doctrine.php"], + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + } +} diff --git a/vendor/doctrine/orm/docs/README.md b/vendor/doctrine/orm/docs/README.md new file mode 100644 index 00000000..4315116f --- /dev/null +++ b/vendor/doctrine/orm/docs/README.md @@ -0,0 +1,8 @@ +# Doctrine ORM Documentation + +## How to Generate + +1. Run ./bin/install-dependencies.sh +2. Run ./bin/generate-docs.sh + +It will generate the documentation into the build directory of the checkout. \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/generate-docs.sh b/vendor/doctrine/orm/docs/bin/generate-docs.sh new file mode 100644 index 00000000..7d06d2a8 --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/generate-docs.sh @@ -0,0 +1,10 @@ +#!/bin/bash +EXECPATH=`dirname $0` +cd $EXECPATH +cd .. + +rm build -Rf +sphinx-build en build + +sphinx-build -b latex en build/pdf +rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/install-dependencies.sh b/vendor/doctrine/orm/docs/bin/install-dependencies.sh new file mode 100644 index 00000000..86b3bdff --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/install-dependencies.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt-get install python25 python25-dev texlive-full rubber +sudo easy_install pygments +sudo easy_install sphinx \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/en/Makefile b/vendor/doctrine/orm/docs/en/Makefile new file mode 100644 index 00000000..a6f6fce6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/Makefile @@ -0,0 +1,89 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/vendor/doctrine/orm/docs/en/_exts/configurationblock.py b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py new file mode 100644 index 00000000..36ca61f5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py @@ -0,0 +1,93 @@ +#Copyright (c) 2010 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. + +from docutils.parsers.rst import Directive, directives +from docutils import nodes +from string import upper + +class configurationblock(nodes.General, nodes.Element): + pass + +class ConfigurationBlock(Directive): + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + formats = { + 'html': 'HTML', + 'xml': 'XML', + 'php': 'PHP', + 'yaml': 'YAML', + 'jinja': 'Twig', + 'html+jinja': 'Twig', + 'jinja+html': 'Twig', + 'php+html': 'PHP', + 'html+php': 'PHP', + 'ini': 'INI', + 'php-annotations': 'Annotations', + } + + def run(self): + env = self.state.document.settings.env + + node = nodes.Element() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + + entries = [] + for i, child in enumerate(node): + if isinstance(child, nodes.literal_block): + # add a title (the language name) before each block + #targetid = "configuration-block-%d" % env.new_serialno('configuration-block') + #targetnode = nodes.target('', '', ids=[targetid]) + #targetnode.append(child) + + innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']]) + + para = nodes.paragraph() + para += [innernode, child] + + entry = nodes.list_item('') + entry.append(para) + entries.append(entry) + + resultnode = configurationblock() + resultnode.append(nodes.bullet_list('', *entries)) + + return [resultnode] + +def visit_configurationblock_html(self, node): + self.body.append(self.starttag(node, 'div', CLASS='configuration-block')) + +def depart_configurationblock_html(self, node): + self.body.append('\n') + +def visit_configurationblock_latex(self, node): + pass + +def depart_configurationblock_latex(self, node): + pass + +def setup(app): + app.add_node(configurationblock, + html=(visit_configurationblock_html, depart_configurationblock_html), + latex=(visit_configurationblock_latex, depart_configurationblock_latex)) + app.add_directive('configuration-block', ConfigurationBlock) diff --git a/vendor/doctrine/orm/docs/en/conf.py b/vendor/doctrine/orm/docs/en/conf.py new file mode 100644 index 00000000..5155ac9a --- /dev/null +++ b/vendor/doctrine/orm/docs/en/conf.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# +# Doctrine 2 ORM documentation build configuration file, created by +# sphinx-quickstart on Fri Dec 3 18:10:24 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('_exts')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['configurationblock'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Doctrine 2 ORM' +copyright = u'2010-12, Doctrine Project Team' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2' +# The full version, including alpha/beta/rc tags. +release = '2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'doctrine' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_theme'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Doctrine2ORMdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation', + u'Doctrine Project Team', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True + +primary_domain = "dcorm" + +def linkcode_resolve(domain, info): + if domain == 'dcorm': + return 'http://' + return None diff --git a/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst new file mode 100644 index 00000000..bc24cd30 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -0,0 +1,256 @@ +Advanced field value conversion using custom mapping types +========================================================== + +.. sectionauthor:: Jan Sorgalla + +When creating entities, you sometimes have the need to transform field values +before they are saved to the database. In Doctrine you can use Custom Mapping +Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`). + +There are several ways to achieve this: converting the value inside the Type +class, converting the value on the database-level or a combination of both. + +This article describes the third way by implementing the MySQL specific column +type `Point `_. + +The ``Point`` type is part of the `Spatial extension `_ +of MySQL and enables you to store a single location in a coordinate space by +using x and y coordinates. You can use the Point type to store a +longitude/latitude pair to represent a geographic location. + +The entity +---------- + +We create a simple entity with a field ``$point`` which holds a value object +``Point`` representing the latitude and longitude of the position. + +The entity class: + +.. code-block:: php + + point = $point; + } + + /** + * @return \Geo\ValueObject\Point + */ + public function getPoint() + { + return $this->point; + } + + /** + * @param string $address + */ + public function setAddress($address) + { + $this->address = $address; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + } + +We use the custom type ``point`` in the ``@Column`` docblock annotation of the +``$point`` field. We will create this custom mapping type in the next chapter. + +The point class: + +.. code-block:: php + + latitude = $latitude; + $this->longitude = $longitude; + } + + /** + * @return float + */ + public function getLatitude() + { + return $this->latitude; + } + + /** + * @return float + */ + public function getLongitude() + { + return $this->longitude; + } + } + +The mapping type +---------------- + +Now we're going to create the ``point`` type and implement all required methods. + +.. code-block:: php + + getLongitude(), $value->getLatitude()); + } + + return $value; + } + + public function canRequireSQLConversion() + { + return true; + } + + public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('AsText(%s)', $sqlExpr); + } + + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('PointFromText(%s)', $sqlExpr); + } + } + +We do a 2-step conversion here. In the first step, we convert the ``Point`` +object into a string representation before saving to the database (in the +``convertToDatabaseValue`` method) and back into an object after fetching the +value from the database (in the ``convertToPHPValue`` method). + +The format of the string representation format is called `Well-known text (WKT) +`_. The advantage of this format +is, that it is both human readable and parsable by MySQL. + +Internally, MySQL stores geometry values in a binary format that is not +identical to the WKT format. So, we need to let MySQL transform the WKT +representation into its internal format. + +This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL`` +methods come into play. + +This methods wrap a sql expression (the WKT representation of the Point) into +MySQL functions `PointFromText `_ +and `AsText `_ +which convert WKT strings to and from the internal format of MySQL. + +.. note:: + + When using DQL queries, the ``convertToPHPValueSQL`` and + ``convertToDatabaseValueSQL`` methods only apply to identification variables + and path expressions in SELECT clauses. Expressions in WHERE clauses are + **not** wrapped! + + If you want to use Point values in WHERE clauses, you have to implement a + :doc:`user defined function ` for + ``PointFromText``. + +Example usage +------------- + +.. code-block:: php + + getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point'); + + // Store a Location object + use Geo\Entity\Location; + use Geo\ValueObject\Point; + + $location = new Location(); + + $location->setAddress('1600 Amphitheatre Parkway, Mountain View, CA'); + $location->setPoint(new Point(37.4220761, -122.0845187)); + + $em->persist($location); + $em->flush(); + $em->clear(); + + // Fetch the Location object + $query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'"); + $location = $query->getSingleResult(); + + /* @var Geo\ValueObject\Point */ + $point = $location->getPoint(); diff --git a/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst new file mode 100644 index 00000000..5d0981b0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst @@ -0,0 +1,376 @@ +Aggregate Fields +================ + +.. sectionauthor:: Benjamin Eberlei + +You will often come across the requirement to display aggregate +values of data that can be computed by using the MIN, MAX, COUNT or +SUM SQL functions. For any ORM this is a tricky issue +traditionally. Doctrine 2 offers several ways to get access to +these values and this article will describe all of them from +different perspectives. + +You will see that aggregate fields can become very explicit +features in your domain model and how this potentially complex +business rules can be easily tested. + +An example model +---------------- + +Say you want to model a bank account and all their entries. Entries +into the account can either be of positive or negative money +values. Each account has a credit limit and the account is never +allowed to have a balance below that value. + +For simplicity we live in a world were money is composed of +integers only. Also we omit the receiver/sender name, stated reason +for transfer and the execution date. These all would have to be +added on the ``Entry`` object. + +Our entities look like: + +.. code-block:: php + + no = $no; + $this->maxCredit = $maxCredit; + $this->entries = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** + * @Entity + */ + class Entry + { + /** @Id @GeneratedValue @Column(type="integer") */ + private $id; + + /** + * @ManyToOne(targetEntity="Account", inversedBy="entries") + */ + private $account; + + /** + * @Column(type="integer") + */ + private $amount; + + public function __construct($account, $amount) + { + $this->account = $account; + $this->amount = $amount; + // more stuff here, from/to whom, stated reason, execution date and such + } + + public function getAmount() + { + return $this->amount; + } + } + +Using DQL +--------- + +The Doctrine Query Language allows you to select for aggregate +values computed from fields of your Domain Model. You can select +the current balance of your account by calling: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $myAccountId) + ->getSingleScalarResult(); + +The ``$em`` variable in this (and forthcoming) example holds the +Doctrine ``EntityManager``. We create a query for the SUM of all +amounts (negative amounts are withdraws) and retrieve them as a +single scalar result, essentially return only the first column of +the first row. + +This approach is simple and powerful, however it has a serious +drawback. We have to execute a specific query for the balance +whenever we need it. + +To implement a powerful domain model we would rather have access to +the balance from our ``Account`` entity during all times (even if +the Account was not persisted in the database before!). + +Also an additional requirement is the max credit per ``Account`` +rule. + +We cannot reliably enforce this rule in our ``Account`` entity with +the DQL retrieval of the balance. There are many different ways to +retrieve accounts. We cannot guarantee that we can execute the +aggregation query for all these use-cases, let alone that a +userland programmer checks this balance against newly added +entries. + +Using your Domain Model +----------------------- + +``Account`` and all the ``Entry`` instances are connected through a +collection, which means we can compute this value at runtime: + +.. code-block:: php + + entries AS $entry) { + $balance += $entry->getAmount(); + } + return $balance; + } + } + +Now we can always call ``Account::getBalance()`` to access the +current account balance. + +To enforce the max credit rule we have to implement the "Aggregate +Root" pattern as described in Eric Evans book on Domain Driven +Design. Described with one sentence, an aggregate root controls the +instance creation, access and manipulation of its children. + +In our case we want to enforce that new entries can only added to +the ``Account`` by using a designated method. The ``Account`` is +the aggregate root of this relation. We can also enforce the +correctness of the bi-directional ``Account`` <-> ``Entry`` +relation with this method: + +.. code-block:: php + + assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + return $e; + } + } + +Now look at the following test-code for our entities: + +.. code-block:: php + + assertEquals(0, $account->getBalance()); + + $account->addEntry(500); + $this->assertEquals(500, $account->getBalance()); + + $account->addEntry(-700); + $this->assertEquals(-200, $account->getBalance()); + } + + public function testExceedMaxLimit() + { + $account = new Account("123456", $maxCredit = 200); + + $this->setExpectedException("Exception"); + $account->addEntry(-1000); + } + } + +To enforce our rule we can now implement the assertion in +``Account::addEntry``: + +.. code-block:: php + + getBalance() + $amount; + $allowedMinimalBalance = ($this->maxCredit * -1); + if ($futureBalance < $allowedMinimalBalance) { + throw new Exception("Credit Limit exceeded, entry is not allowed!"); + } + } + } + +We haven't talked to the entity manager for persistence of our +account example before. You can call +``EntityManager::persist($account)`` and then +``EntityManager::flush()`` at any point to save the account to the +database. All the nested ``Entry`` objects are automatically +flushed to the database also. + +.. code-block:: php + + addEntry(500); + $account->addEntry(-200); + $em->persist($account); + $em->flush(); + +The current implementation has a considerable drawback. To get the +balance, we have to initialize the complete ``Account::$entries`` +collection, possibly a very large one. This can considerably hurt +the performance of your application. + +Using an Aggregate Field +------------------------ + +To overcome the previously mentioned issue (initializing the whole +entries collection) we want to add an aggregate field called +"balance" on the Account and adjust the code in +``Account::getBalance()`` and ``Account:addEntry()``: + +.. code-block:: php + + balance; + } + + public function addEntry($amount) + { + $this->assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + $this->balance += $amount; + return $e; + } + } + +This is a very simple change, but all the tests still pass. Our +account entities return the correct balance. Now calling the +``Account::getBalance()`` method will not occur the overhead of +loading all entries anymore. Adding a new Entry to the +``Account::$entities`` will also not initialize the collection +internally. + +Adding a new entry is therefore very performant and explicitly +hooked into the domain model. It will only update the account with +the current balance and insert the new entry into the database. + +Tackling Race Conditions with Aggregate Fields +---------------------------------------------- + +Whenever you denormalize your database schema race-conditions can +potentially lead to inconsistent state. See this example: + +.. code-block:: php + + find('Bank\Entities\Account', $accId); + + // request 2 account + $account2 = $em->find('Bank\Entities\Account', $accId); + + $account1->addEntry(-200); + $account2->addEntry(-200); + + // now request 1 and 2 both flush the changes. + +The aggregate field ``Account::$balance`` is now -200, however the +SUM over all entries amounts yields -400. A violation of our max +credit rule. + +You can use both optimistic or pessimistic locking to save-guard +your aggregate fields against this kind of race-conditions. Reading +Eric Evans DDD carefully he mentions that the "Aggregate Root" +(Account in our example) needs a locking mechanism. + +Optimistic locking is as easy as adding a version column: + +.. code-block:: php + + find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ); + +Keeping Updates and Deletes in Sync +----------------------------------- + +The example shown in this article does not allow changes to the +value in ``Entry``, which considerably simplifies the effort to +keep ``Account::$balance`` in sync. If your use-case allows fields +to be updated or related entities to be removed you have to +encapsulate this logic in your "Aggregate Root" entity and adjust +the aggregate field accordingly. + +Conclusion +---------- + +This article described how to obtain aggregate values using DQL or +your domain model. It showed how you can easily add an aggregate +field that offers serious performance benefits over iterating all +the related objects that make up an aggregate value. Finally I +showed how you can ensure that your aggregate fields do not get out +of sync due to race-conditions and concurrent access. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/decorator-pattern.rst b/vendor/doctrine/orm/docs/en/cookbook/decorator-pattern.rst new file mode 100644 index 00000000..49d13b71 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/decorator-pattern.rst @@ -0,0 +1,273 @@ +Persisting the Decorator Pattern +================================ + +.. sectionauthor:: Chris Woodford + +This recipe will show you a simple example of how you can use +Doctrine 2 to persist an implementation of the +`Decorator Pattern `_ + +Component +--------- + +The ``Component`` class needs to be persisted, so it's going to +be an ``Entity``. As the top of the inheritance hierarchy, it's going +to have to define the persistent inheritance. For this example, we +will use Single Table Inheritance, but Class Table Inheritance +would work as well. In the discriminator map, we will define two +concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``. + +.. code-block:: php + + id; + } + + /** + * Set name + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get name + * @return string $name + */ + public function getName() + { + return $this->name; + } + + } + +ConcreteComponent +----------------- + +The ``ConcreteComponent`` class is pretty simple and doesn't do much +more than extend the abstract ``Component`` class (only for the +purpose of keeping this example simple). + +.. code-block:: php + + setDecorates($c); + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return 'Decorated ' . $this->getDecorates()->getName(); + } + + /** + * the component being decorated + * @return Component + */ + protected function getDecorates() + { + return $this->decorates; + } + + /** + * sets the component being decorated + * @param Component $c + */ + protected function setDecorates(Component $c) + { + $this->decorates = $c; + } + + } + +All operations on the ``Decorator`` (i.e. persist, remove, etc) will +cascade from the ``Decorator`` to the ``Component``. This means that +when we persist a ``Decorator``, Doctrine will take care of +persisting the chain of decorated objects for us. A ``Decorator`` can +be treated exactly as a ``Component`` when it comes time to +persisting it. + +The ``Decorator's`` constructor accepts an instance of a +``Component``, as defined by the ``Decorator`` pattern. The +setDecorates/getDecorates methods have been defined as protected to +hide the fact that a ``Decorator`` is decorating a ``Component`` and +keeps the ``Component`` interface and the ``Decorator`` interface +identical. + +To illustrate the intended result of the ``Decorator`` pattern, the +getName() method has been overridden to append a string to the +``Component's`` getName() method. + +ConcreteDecorator +----------------- + +The final class required to complete a simple implementation of the +Decorator pattern is the ``ConcreteDecorator``. In order to further +illustrate how the ``Decorator`` can alter data as it moves through +the chain of decoration, a new field, "special", has been added to +this class. The getName() has been overridden and appends the value +of the getSpecial() method to its return value. + +.. code-block:: php + + special = $special; + } + + /** + * Get special + * @return string $special + */ + public function getSpecial() + { + return $this->special; + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return '[' . $this->getSpecial() + . '] ' . parent::getName(); + } + + } + +Examples +-------- + +Here is an example of how to persist and retrieve your decorated +objects + +.. code-block:: php + + setName('Test Component 1'); + $em->persist($c); // assigned unique ID = 1 + + // create a new concrete decorator + $c = new ConcreteComponent(); + $c->setName('Test Component 2'); + + $d = new ConcreteDecorator($c); + $d->setSpecial('Really'); + $em->persist($d); + // assigns c as unique ID = 2, and d as unique ID = 3 + + $em->flush(); + + $c = $em->find('Test\Component', 1); + $d = $em->find('Test\Component', 3); + + echo get_class($c); + // prints: Test\Component\ConcreteComponent + + echo $c->getName(); + // prints: Test Component 1 + + echo get_class($d) + // prints: Test\Component\ConcreteDecorator + + echo $d->getName(); + // prints: [Really] Decorated Test Component 2 + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst new file mode 100644 index 00000000..2887a5d6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst @@ -0,0 +1,217 @@ +Extending DQL in Doctrine 2: Custom AST Walkers +=============================================== + +.. sectionauthor:: Benjamin Eberlei + +The Doctrine Query Language (DQL) is a proprietary sql-dialect that +substitutes tables and columns for Entity names and their fields. +Using DQL you write a query against the database using your +entities. With the help of the metadata you can write very concise, +compact and powerful queries that are then translated into SQL by +the Doctrine ORM. + +In Doctrine 1 the DQL language was not implemented using a real +parser. This made modifications of the DQL by the user impossible. +Doctrine 2 in contrast has a real parser for the DQL language, +which transforms the DQL statement into an +`Abstract Syntax Tree `_ +and generates the appropriate SQL statement for it. Since this +process is deterministic Doctrine heavily caches the SQL that is +generated from any given DQL query, which reduces the performance +overhead of the parsing process to zero. + +You can modify the Abstract syntax tree by hooking into DQL parsing +process by adding a Custom Tree Walker. A walker is an interface +that walks each node of the Abstract syntax tree, thereby +generating the SQL statement. + +There are two types of custom tree walkers that you can hook into +the DQL parser: + + +- An output walker. This one actually generates the SQL, and there + is only ever one of them. We implemented the default SqlWalker + implementation for it. +- A tree walker. There can be many tree walkers, they cannot + generate the sql, however they can modify the AST before its + rendered to sql. + +Now this is all awfully technical, so let me come to some use-cases +fast to keep you motivated. Using walker implementation you can for +example: + + +- Modify the AST to generate a Count Query to be used with a + paginator for any given DQL query. +- Modify the Output Walker to generate vendor-specific SQL + (instead of ANSI). +- Modify the AST to add additional where clauses for specific + entities (example ACL, country-specific content...) +- Modify the Output walker to pretty print the SQL for debugging + purposes. + +In this cookbook-entry I will show examples on the first two +points. There are probably much more use-cases. + +Generic count query for pagination +---------------------------------- + +Say you have a blog and posts all with one category and one author. +A query for the front-page or any archive page might look something +like: + +.. code-block:: sql + + SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now in this query the blog post is the root entity, meaning its the +one that is hydrated directly from the query and returned as an +array of blog posts. In contrast the comment and author are loaded +for deeper use in the object tree. + +A pagination for this query would want to approximate the number of +posts that match the WHERE clause of this query to be able to +predict the number of pages to show to the user. A draft of the DQL +query for pagination would look like: + +.. code-block:: sql + + SELECT count(DISTINCT p.id) FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now you could go and write each of these queries by hand, or you +can use a tree walker to modify the AST for you. Lets see how the +API would look for this use-case: + +.. code-block:: php + + createQuery($dql); + $query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20); + + $totalResults = Paginate::count($query); + $results = $query->getResult(); + +The ``Paginate::count(Query $query)`` looks like: + +.. code-block:: php + + setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker')); + $countQuery->setFirstResult(null)->setMaxResults(null); + + return $countQuery->getSingleScalarResult(); + } + } + +It clones the query, resets the limit clause first and max results +and registers the ``CountSqlWalker`` customer tree walker which +will modify the AST to execute a count query. The walkers +implementation is: + +.. code-block:: php + + _getQueryComponents() AS $dqlAlias => $qComp) { + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + break; + } + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $parent['metadata']->getSingleIdentifierFieldName() + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, true), null + ) + ); + } + } + +This will delete any given select expressions and replace them with +a distinct count query for the root entities primary key. This will +only work if your entity has only one identifier field (composite +keys won't work). + +Modify the Output Walker to generate Vendor specific SQL +-------------------------------------------------------- + +Most RMDBS have vendor-specific features for optimizing select +query execution plans. You can write your own output walker to +introduce certain keywords using the Query Hint API. A query hint +can be set via ``Query::setHint($name, $value)`` as shown in the +previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint. + +We will implement a custom Output Walker that allows to specify the +SQL\_NO\_CACHE query hint. + +.. code-block:: php + + createQuery($dql); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'DoctrineExtensions\Query\MysqlWalker'); + $query->setHint("mysqlWalker.sqlNoCache", true); + $results = $query->getResult(); + +Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will +modify the generation of the SELECT clause, adding the +SQL\_NO\_CACHE on those queries that need it: + +.. code-block:: php + + getQuery()->getHint('mysqlWalker.sqlNoCache') === true) { + if ($selectClause->isDistinct) { + $sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql); + } else { + $sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql); + } + } + + return $sql; + } + } + +Writing extensions to the Output Walker requires a very deep +understanding of the DQL Parser and Walkers, but may offer your +huge benefits with using vendor specific features. This would still +allow you write DQL queries instead of NativeQueries to make use of +vendor specific features. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst new file mode 100644 index 00000000..77d66ad8 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst @@ -0,0 +1,240 @@ +DQL User Defined Functions +========================== + +.. sectionauthor:: Benjamin Eberlei + +By default DQL supports a limited subset of all the vendor-specific +SQL functions common between all the vendors. However in many cases +once you have decided on a specific database vendor, you will never +change it during the life of your project. This decision for a +specific vendor potentially allows you to make use of powerful SQL +features that are unique to the vendor. + +It is worth to mention that Doctrine 2 also allows you to handwrite +your SQL instead of extending the DQL parser. Extending DQL is sort of an +advanced extension point. You can map arbitrary SQL to your objects +and gain access to vendor specific functionalities using the +``EntityManager#createNativeQuery()`` API as described in +the :doc:`Native Query <../reference/native-sql>` chapter. + + +The DQL Parser has hooks to register functions that can then be +used in your DQL queries and transformed into SQL, allowing to +extend Doctrines Query capabilities to the vendors strength. This +post explains the Used-Defined Functions API (UDF) of the Dql +Parser and shows some examples to give you some hints how you would +extend DQL. + +There are three types of functions in DQL, those that return a +numerical value, those that return a string and those that return a +Date. Your custom method has to be registered as either one of +those. The return type information is used by the DQL parser to +check possible syntax errors during the parsing process, for +example using a string function return value in a math expression. + +Registering your own DQL functions +---------------------------------- + +You can register your functions adding them to the ORM +configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The ``$name`` is the name the function will be referred to in the +DQL query. ``$class`` is a string of a class-name which has to +extend ``Doctrine\ORM\Query\Node\FunctionNode``. This is a class +that offers all the necessary API and methods to implement a UDF. + +In this post we will implement some MySql specific Date calculation +methods, which are quite handy in my opinion: + +Date Diff +--------- + +`Mysql's DateDiff function `_ +takes two dates as argument and calculates the difference in days +with ``date1-date2``. + +The DQL parser is a top-down recursive descent parser to generate +the Abstract-Syntax Tree (AST) and uses a TreeWalker approach to +generate the appropriate SQL from the AST. This makes reading the +Parser/TreeWalker code manageable in a finite amount of time. + +The ``FunctionNode`` class I referred to earlier requires you to +implement two methods, one for the parsing process (obviously) +called ``parse`` and one for the TreeWalker process called +``getSql()``. I show you the code for the DateDiff method and +discuss it step by step: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); // (2) + $parser->match(Lexer::T_OPEN_PARENTHESIS); // (3) + $this->firstDateExpression = $parser->ArithmeticPrimary(); // (4) + $parser->match(Lexer::T_COMMA); // (5) + $this->secondDateExpression = $parser->ArithmeticPrimary(); // (6) + $parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3) + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATEDIFF(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', ' . + $this->secondDateExpression->dispatch($sqlWalker) . + ')'; // (7) + } + } + +The Parsing process of the DATEDIFF function is going to find two +expressions the date1 and the date2 values, whose AST Node +representations will be saved in the variables of the DateDiff +FunctionNode instance at (1). + +The parse() method has to cut the function call "DATEDIFF" and its +argument into pieces. Since the parser detects the function using a +lookahead the T\_IDENTIFIER of the function name has to be taken +from the stack (2), followed by a detection of the arguments in +(4)-(6). The opening and closing parenthesis have to be detected +also. This happens during the Parsing process and leads to the +generation of a DateDiff FunctionNode somewhere in the AST of the +dql statement. + +The ``ArithmeticPrimary`` method call is the most common +denominator of valid EBNF tokens taken from the +`DQL EBNF grammar `_ +that matches our requirements for valid input into the DateDiff Dql +function. Picking the right tokens for your methods is a tricky +business, but the EBNF grammar is pretty helpful finding it, as is +looking at the Parser source code. + +Now in the TreeWalker process we have to pick up this node and +generate SQL from it, which apparently is quite easy looking at the +code in (7). Since we don't know which type of AST Node the first +and second Date expression are we are just dispatching them back to +the SQL Walker to generate SQL from and then wrap our DATEDIFF +function call around this output. + +Now registering this DateDiff FunctionNode with the ORM using: + +.. code-block:: php + + addCustomStringFunction('DATEDIFF', 'DoctrineExtensions\Query\MySql\DateDiff'); + +We can do fancy stuff like: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATEDIFF(CURRENT_TIME(), p.created) < 7 + +Date Add +-------- + +Often useful it the ability to do some simple date calculations in +your DQL query using +`MySql's DATE\_ADD function `_. + +I'll skip the blah and show the code for this function: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_IDENTIFIER); + + $this->intervalExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_IDENTIFIER); + + /* @var $lexer Lexer */ + $lexer = $parser->getLexer(); + $this->unit = $lexer->token['value']; + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATE_ADD(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', INTERVAL ' . + $this->intervalExpression->dispatch($sqlWalker) . ' ' . $this->unit . + ')'; + } + } + +The only difference compared to the DATEDIFF here is, we +additionally need the ``Lexer`` to access the value of the +``T_IDENTIFIER`` token for the Date Interval unit, for example the +MONTH in: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATE_ADD(CURRENT_TIME(), INTERVAL 4 MONTH) > p.created + +The above method now only supports the specification using +``INTERVAL``, to also allow a real date in DATE\_ADD we need to add +some decision logic to the parsing process (makes up for a nice +exercise). + +Now as you see, the Parsing process doesn't catch all the possible +SQL errors, here we don't match for all the valid inputs for the +interval unit. However where necessary we rely on the database +vendors SQL parser to show us further errors in the parsing +process, for example if the Unit would not be one of the supported +values by MySql. + +Conclusion +---------- + +Now that you all know how you can implement vendor specific SQL +functionalities in DQL, we would be excited to see user extensions +that add vendor specific function packages, for example more math +functions, XML + GIS Support, Hashing functions and so on. + +For 2.0 we will come with the current set of functions, however for +a future version we will re-evaluate if we can abstract even more +vendor sql functions and extend the DQL languages scope. + +Code for this Extension to DQL and other Doctrine Extensions can be +found +`in my Github DoctrineExtensions repository `_. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst new file mode 100644 index 00000000..664cff53 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst @@ -0,0 +1,68 @@ +Entities in the Session +======================= + +There are several use-cases to save entities in the session, for example: + +1. User object +2. Multi-step forms + +To achieve this with Doctrine you have to pay attention to some details to get +this working. + +Merging entity into an EntityManager +------------------------------------ + +In Doctrine an entity objects has to be "managed" by an EntityManager to be +updateable. Entities saved into the session are not managed in the next request +anymore. This means that you have to register these entities with an +EntityManager again if you want to change them or use them as part of +references between other entities. You can achieve this by calling +``EntityManager#merge()``. + +For a representative User object the code to get turn an instance from +the session into a managed Doctrine object looks like this: + +.. code-block:: php + + merge($user); + } + +.. note:: + + A frequent mistake is not to get the merged user object from the return + value of ``EntityManager#merge()``. The entity object passed to merge is + not necessarily the same object that is returned from the method. + +Serializing entity into the session +----------------------------------- + +Entities that are serialized into the session normally contain references to +other entities as well. Think of the user entity has a reference to his +articles, groups, photos or many other different entities. If you serialize +this object into the session then you don't want to serialize the related +entities as well. This is why you should call ``EntityManager#detach()`` on this +object or implement the __sleep() magic method on your entity. + +.. code-block:: php + + find("User", 1); + $em->detach($user); + $_SESSION['user'] = $user; + +.. note:: + + When you called detach on your objects they get "unmanaged" with that + entity manager. This means you cannot use them as part of write operations + during ``EntityManager#flush()`` anymore in this request. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst new file mode 100644 index 00000000..4eb2b717 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst @@ -0,0 +1,112 @@ +Implementing ArrayAccess for Domain Objects +=========================================== + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +This recipe will show you how to implement ArrayAccess for your +domain objects in order to allow more uniform access, for example +in templates. In these examples we will implement ArrayAccess on a +`Layer Supertype `_ +for all our domain objects. + +Option 1 +-------- + +In this implementation we will make use of PHPs highly dynamic +nature to dynamically access properties of a subtype in a supertype +at runtime. Note that this implementation has 2 main caveats: + + +- It will not work with private fields +- It will not go through any getters/setters + +.. code-block:: php + + $offset); + } + + public function offsetSet($offset, $value) { + $this->$offset = $value; + } + + public function offsetGet($offset) { + return $this->$offset; + } + + public function offsetUnset($offset) { + $this->$offset = null; + } + } + +Option 2 +-------- + +In this implementation we will dynamically invoke getters/setters. +Again we use PHPs dynamic nature to invoke methods on a subtype +from a supertype at runtime. This implementation has the following +caveats: + + +- It relies on a naming convention +- The semantics of offsetExists can differ +- offsetUnset will not work with typehinted setters + +.. code-block:: php + + {"get$offset"}(); + return $value !== null; + } + + public function offsetSet($offset, $value) { + $this->{"set$offset"}($value); + } + + public function offsetGet($offset) { + return $this->{"get$offset"}(); + } + + public function offsetUnset($offset) { + $this->{"set$offset"}(null); + } + } + +Read-only +--------- + +You can slightly tweak option 1 or option 2 in order to make array +access read-only. This will also circumvent some of the caveats of +each option. Simply make offsetSet and offsetUnset throw an +exception (i.e. BadMethodCallException). + +.. code-block:: php + + `_ +for all our domain objects. + +Implementing NotifyPropertyChanged +---------------------------------- + +The NOTIFY policy is based on the assumption that the entities +notify interested listeners of changes to their properties. For +that purpose, a class that wants to use this policy needs to +implement the ``NotifyPropertyChanged`` interface from the +``Doctrine\Common`` namespace. + +.. code-block:: php + + listeners[] = $listener; + } + + /** Notifies listeners of a change. */ + protected function onPropertyChanged($propName, $oldValue, $newValue) { + if ($this->listeners) { + foreach ($this->listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + } + +Then, in each property setter of concrete, derived domain classes, +you need to invoke onPropertyChanged as follows to notify +listeners: + +.. code-block:: php + + data) { // check: is it actually modified? + $this->onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you can avoid unnecessary +updates and also have full control over when you consider a +property changed. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst new file mode 100644 index 00000000..6a8ef9c9 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst @@ -0,0 +1,78 @@ +Implementing Wakeup or Clone +============================ + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +As explained in the +`restrictions for entity classes in the manual `_, +it is usually not allowed for an entity to implement ``__wakeup`` +or ``__clone``, because Doctrine makes special use of them. +However, it is quite easy to make use of these methods in a safe +way by guarding the custom wakeup or clone code with an entity +identity check, as demonstrated in the following sections. + +Safely implementing \_\_wakeup +------------------------------ + +To safely implement ``__wakeup``, simply enclose your +implementation code in an identity check as follows: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Safely implementing \_\_clone +----------------------------- + +Safely implementing ``__clone`` is pretty much the same: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Summary +------- + +As you have seen, it is quite easy to safely make use of +``__wakeup`` and ``__clone`` in your entities without adding any +really Doctrine-specific or Doctrine-dependant code. + +These implementations are possible and safe because when Doctrine +invokes these methods, the entities never have an identity (yet). +Furthermore, it is possibly a good idea to check for the identity +in your code anyway, since it's rarely the case that you want to +unserialize or clone an entity with no identity. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst new file mode 100644 index 00000000..1c06a34e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst @@ -0,0 +1,140 @@ +Integrating with CodeIgniter +============================ + +This is recipe for using Doctrine 2 in your +`CodeIgniter `_ framework. + +.. note:: + + This might not work for all CodeIgniter versions and may require + slight adjustments. + + +Here is how to set it up: + +Make a CodeIgniter library that is both a wrapper and a bootstrap +for Doctrine 2. + +Setting up the file structure +----------------------------- + +Here are the steps: + + +- Add a php file to your system/application/libraries folder + called Doctrine.php. This is going to be your wrapper/bootstrap for + the D2 entity manager. +- Put the Doctrine folder (the one that contains Common, DBAL, and + ORM) inside that same libraries folder. +- Your system/application/libraries folder now looks like this: + + system/applications/libraries -Doctrine -Doctrine.php -index.html + +- If you want, open your config/autoload.php file and autoload + your Doctrine library. + + register(); + $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" )); + $entitiesClassLoader->register(); + $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies'); + $proxiesClassLoader->register(); + + // Set up caches + $config = new Configuration; + $cache = new ArrayCache; + $config->setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities')); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + + $config->setQueryCacheImpl($cache); + + // Proxy configuration + $config->setProxyDir(APPPATH.'/models/proxies'); + $config->setProxyNamespace('Proxies'); + + // Set up logger + $logger = new EchoSQLLogger; + $config->setSQLLogger($logger); + + $config->setAutoGenerateProxyClasses( TRUE ); + + // Database connection information + $connectionOptions = array( + 'driver' => 'pdo_mysql', + 'user' => $db['default']['username'], + 'password' => $db['default']['password'], + 'host' => $db['default']['hostname'], + 'dbname' => $db['default']['database'] + ); + + // Create EntityManager + $this->em = EntityManager::create($connectionOptions, $config); + } + } + +Please note that this is a development configuration; for a +production system you'll want to use a real caching system like +APC, get rid of EchoSqlLogger, and turn off +autoGenerateProxyClasses. + +For more details, consult the +`Doctrine 2 Configuration documentation `_. + +Now to use it +------------- + +Whenever you need a reference to the entity manager inside one of +your controllers, views, or models you can do this: + +.. code-block:: php + + doctrine->em; + +That's all there is to it. Once you get the reference to your +EntityManager do your Doctrine 2.0 voodoo as normal. + +Note: If you do not choose to autoload the Doctrine library, you +will need to put this line before you get a reference to it: + +.. code-block:: php + + load->library('doctrine'); + +Good luck! + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst new file mode 100644 index 00000000..69f0a351 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst @@ -0,0 +1,189 @@ +Mysql Enums +=========== + +The type system of Doctrine 2 consists of flyweights, which means there is only +one instance of any given type. Additionally types do not contain state. Both +assumptions make it rather complicated to work with the Enum Type of MySQL that +is used quite a lot by developers. + +When using Enums with a non-tweaked Doctrine 2 application you will get +errors from the Schema-Tool commands due to the unknown database type "enum". +By default Doctrine does not map the MySQL enum type to a Doctrine type. +This is because Enums contain state (their allowed values) and Doctrine +types don't. + +This cookbook entry shows two possible solutions to work with MySQL enums. +But first a word of warning. The MySQL Enum type has considerable downsides: + +- Adding new values requires to rebuild the whole table, which can take hours + depending on the size. +- Enums are ordered in the way the values are specified, not in their "natural" order. +- Enums validation mechanism for allowed values is not necessarily good, + specifying invalid values leads to an empty enum for the default MySQL error + settings. You can easily replicate the "allow only some values" requirement + in your Doctrine entities. + +Solution 1: Mapping to Varchars +------------------------------- + +You can map ENUMs to varchars. You can register MySQL ENUMs to map to Doctrine +varchars. This way Doctrine always resolves ENUMs to Doctrine varchars. It +will even detect this match correctly when using SchemaTool update commands. + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); + +In this case you have to ensure that each varchar field that is an enum in the +database only gets passed the allowed values. You can easily enforce this in your +entities: + +.. code-block:: php + + status = $status; + } + } + +If you want to actively create enums through the Doctrine Schema-Tool by using +the **columnDefinition** attribute. + +.. code-block:: php + + values); + + return "ENUM(".implode(", ", $values).") COMMENT '(DC2Type:".$this->name.")'"; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!in_array($value, $this->values)) { + throw new \InvalidArgumentException("Invalid '".$this->name."' value."); + } + return $value; + } + + public function getName() + { + return $this->name; + } + } + +With this base class you can define an enum as easily as: + +.. code-block:: php + + addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array()); + + // Add the ResolveTargetEntityListener + $evm->addEventSubscriber($rtel); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + +Final Thoughts +-------------- + +With the ``ResolveTargetEntityListener``, we are able to decouple our +bundles, keeping them usable by themselves, but still being able to +define relationships between different objects. By using this method, +I've found my bundles end up being easier to maintain independently. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst new file mode 100644 index 00000000..6ca73c27 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst @@ -0,0 +1,80 @@ +SQL-Table Prefixes +================== + +This recipe is intended as an example of implementing a +loadClassMetadata listener to provide a Table Prefix option for +your application. The method used below is not a hack, but fully +integrates into the Doctrine system, all SQL generated will include +the appropriate table prefix. + +In most circumstances it is desirable to separate different +applications into individual databases, but in certain cases, it +may be beneficial to have a table prefix for your Entities to +separate them from other vendor products in the same database. + +Implementing the listener +------------------------- + +The listener in this example has been set up with the +DoctrineExtensions namespace. You create this file in your +library/DoctrineExtensions directory, but will need to set up +appropriate autoloaders. + +.. code-block:: php + + prefix = (string) $prefix; + } + + public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + $classMetadata->setTableName($this->prefix . $classMetadata->getTableName()); + foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { + if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY) { + $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; + $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; + } + } + } + + } + +Telling the EntityManager about our listener +-------------------------------------------- + +A listener of this type must be set up before the EntityManager has +been initialised, otherwise an Entity might be created or cached +before the prefix has been set. + +.. note:: + + If you set this listener up, be aware that you will need + to clear your caches and drop then recreate your database schema. + + +.. code-block:: php + + addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst new file mode 100644 index 00000000..d9934f57 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst @@ -0,0 +1,254 @@ +Strategy-Pattern +================ + +This recipe will give you a short introduction on how to design +similar entities without using expensive (i.e. slow) inheritance +but with not more than \* the well-known strategy pattern \* event +listeners + +Scenario / Problem +------------------ + +Given a Content-Management-System, we probably want to add / edit +some so-called "blocks" and "panels". What are they for? + + +- A block might be a registration form, some text content, a table + with information. A good example might also be a small calendar. +- A panel is by definition a block that can itself contain blocks. + A good example for a panel might be a sidebar box: You could easily + add a small calendar into it. + +So, in this scenario, when building your CMS, you will surely add +lots of blocks and panels to your pages and you will find yourself +highly uncomfortable because of the following: + + +- Every existing page needs to know about the panels it contains - + therefore, you'll have an association to your panels. But if you've + got several types of panels - what do you do? Add an association to + every panel-type? This wouldn't be flexible. You might be tempted + to add an AbstractPanelEntity and an AbstractBlockEntity that use + class inheritance. Your page could then only confer to the + AbstractPanelType and Doctrine 2 would do the rest for you, i.e. + load the right entities. But - you'll for sure have lots of panels + and blocks, and even worse, you'd have to edit the discriminator + map *manually* every time you or another developer implements a new + block / entity. This would tear down any effort of modular + programming. + +Therefore, we need something that's far more flexible. + +Solution +-------- + +The solution itself is pretty easy. We will have one base class +that will be loaded via the page and that has specific behaviour - +a Block class might render the front-end and even the backend, for +example. Now, every block that you'll write might look different or +need different data - therefore, we'll offer an API to these +methods but internally, we use a strategy that exactly knows what +to do. + +First of all, we need to make sure that we have an interface that +contains every needed action. Such actions would be rendering the +front-end or the backend, solving dependencies (blocks that are +supposed to be placed in the sidebar could refuse to be placed in +the middle of your page, for example). + +Such an interface could look like this: + + +.. code-block:: php + + blockStrategy. Will not be persisted by Doctrine 2. + * + * @var BlockStrategyInterface + */ + protected $strategyInstance; + + /** + * Returns the strategy that is used for this blockitem. + * + * The strategy itself defines how this block can be rendered etc. + * + * @return string + */ + public function getStrategyClassName() { + return $this->strategyClassName; + } + + /** + * Returns the instantiated strategy + * + * @return BlockStrategyInterface + */ + public function getStrategyInstance() { + return $this->strategyInstance; + } + + /** + * Sets the strategy this block / panel should work as. Make sure that you've used + * this method before persisting the block! + * + * @param BlockStrategyInterface $strategy + */ + public function setStrategy(BlockStrategyInterface $strategy) { + $this->strategyInstance = $strategy; + $this->strategyClassName = get_class($strategy); + $strategy->setBlockEntity($this); + } + +Now, the important point is that $strategyClassName is a Doctrine 2 +field, i.e. Doctrine will persist this value. This is only the +class name of your strategy and not an instance! + +Finishing your strategy pattern, we hook into the Doctrine postLoad +event and check whether a block has been loaded. If so, you will +initialize it - i.e. get the strategies classname, create an +instance of it and set it via setStrategyBlock(). + +This might look like this: + +.. code-block:: php + + view = $view; + } + + public function getSubscribedEvents() { + return array(ORM\Events::postLoad); + } + + public function postLoad(ORM\Event\LifecycleEventArgs $args) { + $blockItem = $args->getEntity(); + + // Both blocks and panels are instances of Block\AbstractBlock + if ($blockItem instanceof Block\AbstractBlock) { + $strategy = $blockItem->getStrategyClassName(); + $strategyInstance = new $strategy(); + if (null !== $blockItem->getConfig()) { + $strategyInstance->setConfig($blockItem->getConfig()); + } + $strategyInstance->setView($this->view); + $blockItem->setStrategy($strategyInstance); + } + } + } + +In this example, even some variables are set - like a view object +or a specific configuration object. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst new file mode 100644 index 00000000..a09b218e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst @@ -0,0 +1,137 @@ +Validation of Entities +====================== + +.. sectionauthor:: Benjamin Eberlei + +Doctrine 2 does not ship with any internal validators, the reason +being that we think all the frameworks out there already ship with +quite decent ones that can be integrated into your Domain easily. +What we offer are hooks to execute any kind of validation. + +.. note:: + + You don't need to validate your entities in the lifecycle + events. Its only one of many options. Of course you can also + perform validations in value setters or any other method of your + entities that are used in your code. + + +Entities can register lifecycle event methods with Doctrine that +are called on different occasions. For validation we would need to +hook into the events called before persisting and updating. Even +though we don't support validation out of the box, the +implementation is even simpler than in Doctrine 1 and you will get +the additional benefit of being able to re-use your validation in +any other part of your domain. + +Say we have an ``Order`` with several ``OrderLine`` instances. We +never want to allow any customer to order for a larger sum than he +is allowed to: + +.. code-block:: php + + customer->getOrderLimit(); + + $amount = 0; + foreach ($this->orderLines AS $line) { + $amount += $line->getAmount(); + } + + if ($amount > $orderLimit) { + throw new CustomerOrderLimitExceededException(); + } + } + } + +Now this is some pretty important piece of business logic in your +code, enforcing it at any time is important so that customers with +a unknown reputation don't owe your business too much money. + +We can enforce this constraint in any of the metadata drivers. +First Annotations: + +.. code-block:: php + + + + + + + + + + +YAML needs some little change yet, to allow multiple lifecycle +events for one method, this will happen before Beta 1 though. + +Now validation is performed whenever you call +``EntityManager#persist($order)`` or when you call +``EntityManager#flush()`` and an order is about to be updated. Any +Exception that happens in the lifecycle callbacks will be cached by +the EntityManager and the current transaction is rolled back. + +Of course you can do any type of primitive checks, not null, +email-validation, string size, integer and date ranges in your +validation callbacks. + +.. code-block:: php + + plannedShipDate instanceof DateTime)) { + throw new ValidateException(); + } + + if ($this->plannedShipDate->format('U') < time()) { + throw new ValidateException(); + } + + if ($this->customer == null) { + throw new OrderRequiresCustomerException(); + } + } + } + +What is nice about lifecycle events is, you can also re-use the +methods at other places in your domain, for example in combination +with your form library. Additionally there is no limitation in the +number of methods you register on one particular event, i.e. you +can register multiple methods for validation in "PrePersist" or +"PreUpdate" or mix and share them in any combinations between those +two events. + +There is no limit to what you can and can't validate in +"PrePersist" and "PreUpdate" as long as you don't create new entity +instances. This was already discussed in the previous blog post on +the Versionable extension, which requires another type of event +called "onFlush". + +Further readings: :doc:`Lifecycle Events <../reference/events>` diff --git a/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst new file mode 100644 index 00000000..fc548dac --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst @@ -0,0 +1,168 @@ +Working with DateTime Instances +=============================== + +There are many nitty gritty details when working with PHPs DateTime instances. You have know their inner +workings pretty well not to make mistakes with date handling. This cookbook entry holds several +interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2. + +DateTime changes are detected by Reference +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When calling ``EntityManager#flush()`` Doctrine computes the changesets of all the currently managed entities +and saves the differences to the database. In case of object properties (@Column(type="datetime") or @Column(type="object")) +these comparisons are always made **BY REFERENCE**. That means the following change will **NOT** be saved into the database: + +.. code-block:: php + + updated->modify("now"); + } + } + +The way to go would be: + +.. code-block:: php + + updated = new \DateTime("now"); + } + } + +Default Timezone Gotcha +~~~~~~~~~~~~~~~~~~~~~~~ + +By default Doctrine assumes that you are working with a default timezone. Each DateTime instance that +is created by Doctrine will be assigned the timezone that is currently the default, either through +the ``date.timezone`` ini setting or by calling ``date_default_timezone_set()``. + +This is very important to handle correctly if your application runs on different serves or is moved from one to another server +(with different timezone settings). You have to make sure that the timezone is the correct one +on all this systems. + +Handling different Timezones with the DateTime Type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you first come across the requirement to save different you are still optimistic to manage this mess, +however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2) +that supports timezones correctly. Correctly here means that you can cover all the use-cases that +can come up with timezones. If you don't believe me you should read up on `Storing DateTime +in Databases `_. + +The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC. +However with frequent daylight saving and political timezone changes you can have a UTC offset that moves +in different offset directions depending on the real location. + +The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround +that even allows correct date-time handling with timezones: + +1. Always convert any DateTime instance to UTC. +2. Only set Timezones for displaying purposes +3. Save the Timezone in the Entity for persistence. + +Say we have an application for an international postal company and employees insert events regarding postal-package +around the world, in their current timezones. To determine the exact time an event occurred means to save both +the UTC time at the time of the booking and the timezone the event happened in. + +.. code-block:: php + + format($platform->getDateTimeFormatString(), + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat( + $platform->getDateTimeFormatString(), + $value, + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + if (!$val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + } + +This database type makes sure that every DateTime instance is always saved in UTC, relative +to the current timezone that the passed DateTime instance has. To be able to transform these values +back into their real timezone you have to save the timezone in a separate field of the entity +requiring timezoned datetimes: + +.. code-block:: php + + localized = true; + $this->created = $createDate; + $this->timezone = $createDate->getTimeZone()->getName(); + } + + public function getCreated() + { + if (!$this->localized) { + $this->created->setTimeZone(new \DateTimeZone($this->timezone)); + } + return $this->created; + } + } + +This snippet makes use of the previously discussed "changeset by reference only" property of +objects. That means a new DateTime will only be used during updating if the reference +changes between retrieval and flush operation. This means we can easily go and modify +the instance by setting the previous local timezone. diff --git a/vendor/doctrine/orm/docs/en/index.rst b/vendor/doctrine/orm/docs/en/index.rst new file mode 100644 index 00000000..e71ea7a4 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/index.rst @@ -0,0 +1,122 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +The Doctrine documentation is comprised of tutorials, a reference section and +cookbook articles that explain different parts of the Object Relational mapper. + +Doctrine DBAL and Doctrine Common both have their own documentation. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about +Doctrine ORM don't panic. You can get help from different sources: + +- There is a :doc:`FAQ ` with answers to frequent questions. +- The `Doctrine Mailing List `_ +- Internet Relay Chat (IRC) in `#doctrine on Freenode `_ +- Report a bug on `JIRA `_. +- On `Twitter `_ with ``#doctrine2`` +- On `StackOverflow `_ + +If you need more structure over the different topics you can browse the :doc:`table +of contents `. + +Getting Started +--------------- + +* **Tutorial**: + :doc:`Getting Started with Doctrine ` + +* **Setup**: + :doc:`Installation & Configuration ` + +Mapping Objects onto a Database +------------------------------- + +* **Mapping**: + :doc:`Objects ` | + :doc:`Associations ` | + :doc:`Inheritance ` + +* **Drivers**: + :doc:`Docblock Annotations ` | + :doc:`XML ` | + :doc:`YAML ` | + :doc:`PHP ` + +Working with Objects +-------------------- + +* **Basic Reference**: + :doc:`Entities ` | + :doc:`Associations ` | + :doc:`Events ` + +* **Query Reference**: + :doc:`DQL ` | + :doc:`QueryBuilder ` | + :doc:`Native SQL ` + +* **Internals**: + :doc:`Internals explained ` | + :doc:`Associations ` + +Advanced Topics +--------------- + + * :doc:`Architecture ` + * :doc:`Advanced Configuration ` + * :doc:`Limitations and knowns issues ` + * :doc:`Commandline Tools ` + * :doc:`Transactions and Concurrency ` + * :doc:`Filters ` + * :doc:`NamingStrategy ` + * :doc:`Improving Performance ` + * :doc:`Caching ` + * :doc:`Partial Objects ` + * :doc:`Change Tracking Policies ` + * :doc:`Best Practices ` + * :doc:`Metadata Drivers ` + +Tutorials +--------- + + * :doc:`Indexed associations ` + * :doc:`Extra Lazy Associations ` + * :doc:`Composite Primary Keys ` + * :doc:`Ordered associations ` + * :doc:`Pagination ` + * :doc:`Override Field/Association Mappings In Subclasses ` + +Cookbook +-------- + +* **Patterns**: + :doc:`Aggregate Fields ` | + :doc:`Decorator Pattern ` | + :doc:`Strategy Pattern ` + +* **DQL Extension Points**: + :doc:`DQL Custom Walkers ` | + :doc:`DQL User-Defined-Functions ` + +* **Implementation**: + :doc:`Array Access ` | + :doc:`Notify ChangeTracking Example ` | + :doc:`Using Wakeup Or Clone ` | + :doc:`Working with DateTime ` | + :doc:`Validation ` | + :doc:`Entities in the Session ` | + :doc:`Keeping your Modules independent ` + +* **Integration into Frameworks/Libraries** + :doc:`CodeIgniter ` + +* **Hidden Gems** + :doc:`Prefixing Table Name ` + +* **Custom Datatypes** + :doc:`MySQL Enums ` + :doc:`Advanced Field Value Conversion ` + diff --git a/vendor/doctrine/orm/docs/en/make.bat b/vendor/doctrine/orm/docs/en/make.bat new file mode 100644 index 00000000..53c40c91 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/make.bat @@ -0,0 +1,113 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +set SPHINXBUILD=sphinx-build +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst new file mode 100644 index 00000000..5c8ad01d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst @@ -0,0 +1,432 @@ +Advanced Configuration +====================== + +The configuration of the EntityManager requires a +``Doctrine\ORM\Configuration`` instance as well as some database +connection parameters. This example shows all the potential +steps of configuration. + +.. code-block:: php + + setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + $config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies'); + $config->setProxyNamespace('MyProject\Proxies'); + + if ($applicationMode == "development") { + $config->setAutoGenerateProxyClasses(true); + } else { + $config->setAutoGenerateProxyClasses(false); + } + + $connectionOptions = array( + 'driver' => 'pdo_sqlite', + 'path' => 'database.sqlite' + ); + + $em = EntityManager::create($connectionOptions, $config); + +.. note:: + + Do not use Doctrine without a metadata and query cache! + Doctrine is optimized for working with caches. The main + parts in Doctrine that are optimized for caching are the metadata + mapping information with the metadata cache and the DQL to SQL + conversions with the query cache. These 2 caches require only an + absolute minimum of memory yet they heavily improve the runtime + performance of Doctrine. The recommended cache driver to use with + Doctrine is `APC `_. APC provides you with + an opcode-cache (which is highly recommended anyway) and a very + fast in-memory cache storage that you can use for the metadata and + query caches as seen in the previous code snippet. + +Configuration Options +--------------------- + +The following sections describe all the configuration options +available on a ``Doctrine\ORM\Configuration`` instance. + +Proxy Directory (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyDir($dir); + $config->getProxyDir(); + +Gets or sets the directory where Doctrine generates any proxy +classes. For a detailed explanation on proxy classes and how they +are used in Doctrine, refer to the "Proxy Objects" section further +down. + +Proxy Namespace (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyNamespace($namespace); + $config->getProxyNamespace(); + +Gets or sets the namespace to use for generated proxy classes. For +a detailed explanation on proxy classes and how they are used in +Doctrine, refer to the "Proxy Objects" section further down. + +Metadata Driver (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataDriverImpl($driver); + $config->getMetadataDriverImpl(); + +Gets or sets the metadata driver implementation that is used by +Doctrine to acquire the object-relational metadata for your +classes. + +There are currently 4 available implementations: + + +- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` +- ``Doctrine\ORM\Mapping\Driver\XmlDriver`` +- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` +- ``Doctrine\ORM\Mapping\Driver\DriverChain`` + +Throughout the most part of this manual the AnnotationDriver is +used in the examples. For information on the usage of the XmlDriver +or YamlDriver please refer to the dedicated chapters +``XML Mapping`` and ``YAML Mapping``. + +The annotation driver can be configured with a factory method on +the ``Doctrine\ORM\Configuration``: + +.. code-block:: php + + newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + +The path information to the entities is required for the annotation +driver, because otherwise mass-operations on all entities through +the console could not work correctly. All of metadata drivers +accept either a single directory as a string or an array of +directories. With this feature a single driver can support multiple +directories of Entities. + +Metadata Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataCacheImpl($cache); + $config->getMetadataCacheImpl(); + +Gets or sets the cache implementation to use for caching metadata +information, that is, all the information you supply via +annotations, xml or yaml, so that they do not need to be parsed and +loaded from scratch on every single request which is a waste of +resources. The cache implementation must implement the +``Doctrine\Common\Cache\Cache`` interface. + +Usage of a metadata cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +Query Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setQueryCacheImpl($cache); + $config->getQueryCacheImpl(); + +Gets or sets the cache implementation to use for caching DQL +queries, that is, the result of a DQL parsing process that includes +the final SQL as well as meta information about how to process the +SQL result set of a query. Note that the query cache does not +affect query results. You do not get stale data. This is a pure +optimization cache without any negative side-effects (except some +minimal memory usage in your cache). + +Usage of a query cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +SQL Logger (***Optional***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setSQLLogger($logger); + $config->getSQLLogger(); + +Gets or sets the logger to use for logging all SQL statements +executed by Doctrine. The logger class must implement the +``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default +implementation that logs to the standard output using ``echo`` and +``var_dump`` can be found at +``Doctrine\DBAL\Logging\EchoSQLLogger``. + +Auto-generating Proxy Classes (***OPTIONAL***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setAutoGenerateProxyClasses($bool); + $config->getAutoGenerateProxyClasses(); + +Gets or sets whether proxy classes should be generated +automatically at runtime by Doctrine. If set to ``FALSE``, proxy +classes must be generated manually through the doctrine command +line task ``generate-proxies``. The strongly recommended value for +a production environment is ``FALSE``. + +Development vs Production Configuration +--------------------------------------- + +You should code your Doctrine2 bootstrapping with two different +runtime models in mind. There are some serious benefits of using +APC or Memcache in production. In development however this will +frequently give you fatal errors, when you change your entities and +the cache still keeps the outdated metadata. That is why we +recommend the ``ArrayCache`` for development. + +Furthermore you should have the Auto-generating Proxy Classes +option to true in development and to false in production. If this +option is set to ``TRUE`` it can seriously hurt your script +performance if several proxy classes are re-generated during script +execution. Filesystem calls of that magnitude can even slower than +all the database queries Doctrine issues. Additionally writing a +proxy sets an exclusive file lock which can cause serious +performance bottlenecks in systems with regular concurrent +requests. + +Connection Options +------------------ + +The ``$connectionOptions`` passed as the first argument to +``EntityManager::create()`` has to be either an array or an +instance of ``Doctrine\DBAL\Connection``. If an array is passed it +is directly passed along to the DBAL Factory +``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL +configuration is explained in the +`DBAL section <./../../../../../dbal/2.0/docs/reference/configuration/en>`_. + +Proxy Objects +------------- + +A proxy object is an object that is put in place or used instead of +the "real" object. A proxy object can add behavior to the object +being proxied without that object being aware of it. In Doctrine 2, +proxy objects are used to realize several features but mainly for +transparent lazy-loading. + +Proxy objects with their lazy-loading facilities help to keep the +subset of objects that are already in memory connected to the rest +of the objects. This is an essential property as without it there +would always be fragile partial objects at the outer edges of your +object graph. + +Doctrine 2 implements a variant of the proxy pattern where it +generates classes that extend your entity classes and adds +lazy-loading capabilities to them. Doctrine can then give you an +instance of such a proxy class whenever you request an object of +the class being proxied. This happens in two situations: + +Reference Proxies +~~~~~~~~~~~~~~~~~ + +The method ``EntityManager#getReference($entityName, $identifier)`` +lets you obtain a reference to an entity for which the identifier +is known, without loading that entity from the database. This is +useful, for example, as a performance enhancement, when you want to +establish an association to an entity for which you have the +identifier. You could simply do this: + +.. code-block:: php + + getReference('MyProject\Model\Item', $itemId); + $cart->addItem($item); + +Here, we added an Item to a Cart without loading the Item from the +database. If you invoke any method on the Item instance, it would +fully initialize its state transparently from the database. Here +$item is actually an instance of the proxy class that was generated +for the Item class but your code does not need to care. In fact it +**should not care**. Proxy objects should be transparent to your +code. + +Association proxies +~~~~~~~~~~~~~~~~~~~ + +The second most important situation where Doctrine uses proxy +objects is when querying for objects. Whenever you query for an +object that has a single-valued association to another object that +is configured LAZY, without joining that association in the same +query, Doctrine puts proxy objects in place where normally the +associated object would be. Just like other proxies it will +transparently initialize itself on first access. + +.. note:: + + Joining an association in a DQL or native query + essentially means eager loading of that association in that query. + This will override the 'fetch' option specified in the mapping for + that association, but only for that query. + + +Generating Proxy classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Proxy classes can either be generated manually through the Doctrine +Console or automatically by Doctrine. The configuration option that +controls this behavior is: + +.. code-block:: php + + setAutoGenerateProxyClasses($bool); + $config->getAutoGenerateProxyClasses(); + +The default value is ``TRUE`` for convenient development. However, +this setting is not optimal for performance and therefore not +recommended for a production environment. To eliminate the overhead +of proxy class generation during runtime, set this configuration +option to ``FALSE``. When you do this in a development environment, +note that you may get class/file not found errors if certain proxy +classes are not available or failing lazy-loads if new methods were +added to the entity class that are not yet in the proxy class. In +such a case, simply use the Doctrine Console to (re)generate the +proxy classes like so: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +Autoloading Proxies +------------------- + +When you deserialize proxy objects from the session or any other storage +it is necessary to have an autoloading mechanism in place for these classes. +For implementation reasons Proxy class names are not PSR-0 compliant. This +means that you have to register a special autoloader for these classes: + +.. code-block:: php + + addDriver($xmlDriver, 'Doctrine\Tests\Models\Company'); + $chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping'); + +Based on the namespace of the entity the loading of entities is +delegated to the appropriate driver. The chain semantics come from +the fact that the driver loops through all namespaces and matches +the entity class name against the namespace using a +``strpos() === 0`` call. This means you need to order the drivers +correctly if sub-namespaces use different metadata driver +implementations. + + +Default Repository (***OPTIONAL***) +----------------------------------- + +Specifies the FQCN of a subclass of the EntityRepository. +That will be available for all entities without a custom repository class. + +.. code-block:: php + + setDefaultRepositoryClassName($fqcn); + $config->getDefaultRepositoryClassName(); + +The default value is ``Doctrine\ORM\EntityRepository``. +Any repository class must be a subclass of EntityRepository otherwise you got an ORMException + +Setting up the Console +---------------------- + +Doctrine uses the Symfony Console component for generating the command +line interface. You can take a look at the ``vendor/bin/doctrine.php`` +script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command +for inspiration how to setup the cli. + +In general the required code looks like this: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli); + $cli->run(); + diff --git a/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst new file mode 100644 index 00000000..d42fda4e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst @@ -0,0 +1,1114 @@ +Annotations Reference +===================== + +In this chapter a reference of every Doctrine 2 Annotation is given +with short explanations on their context and usage. + +Index +----- + +- :ref:`@Column ` +- :ref:`@ColumnResult ` +- :ref:`@ChangeTrackingPolicy ` +- :ref:`@DiscriminatorColumn ` +- :ref:`@DiscriminatorMap ` +- :ref:`@Entity ` +- :ref:`@EntityResult ` +- :ref:`@FieldResult ` +- :ref:`@GeneratedValue ` +- :ref:`@HasLifecycleCallbacks ` +- :ref:`@Index ` +- :ref:`@Id ` +- :ref:`@InheritanceType ` +- :ref:`@JoinColumn ` +- :ref:`@JoinColumns ` +- :ref:`@JoinTable ` +- :ref:`@ManyToOne ` +- :ref:`@ManyToMany ` +- :ref:`@MappedSuperclass ` +- :ref:`@NamedNativeQuery ` +- :ref:`@OneToOne ` +- :ref:`@OneToMany ` +- :ref:`@OrderBy ` +- :ref:`@PostLoad ` +- :ref:`@PostPersist ` +- :ref:`@PostRemove ` +- :ref:`@PostUpdate ` +- :ref:`@PrePersist ` +- :ref:`@PreRemove ` +- :ref:`@PreUpdate ` +- :ref:`@SequenceGenerator ` +- :ref:`@SqlResultSetMapping ` +- :ref:`@Table ` +- :ref:`@UniqueConstraint ` +- :ref:`@Version ` + +Reference +--------- + +.. _annref_column: + +@Column +~~~~~~~ + +Marks an annotated instance variable as "persistent". It has to be +inside the instance variables PHP DocBlock comment. Any value hold +inside this variable will be saved to and loaded from the database +as part of the lifecycle of the instance variables entity-class. + +Required attributes: + +- **type**: Name of the Doctrine Type which is converted between PHP + and Database representation. + +Optional attributes: + +- **name**: By default the property name is used for the database + column name also, however the 'name' attribute allows you to + determine the column name. + +- **length**: Used by the "string" type to determine its maximum + length in the database. Doctrine does not validate the length of a + string values for you. + +- **precision**: The precision for a decimal (exact numeric) column + (Applies only for decimal column) + +- **scale**: The scale for a decimal (exact numeric) column (Applies + only for decimal column) + +- **unique**: Boolean value to determine if the value of the column + should be unique across all rows of the underlying entities table. + +- **nullable**: Determines if NULL values allowed for this column. + +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute allows to make use of advanced RMDBS features. + However you should make careful use of this feature and the + consequences. SchemaTool will not detect changes on the column correctly + anymore if you use "columnDefinition". + + Additionally you should remember that the "type" + attribute still handles the conversion between PHP and Database + values. If you use this attribute on a column that is used for + joins between tables you should also take a look at + :ref:`@JoinColumn `. + +Examples: + +.. code-block:: php + + ` +can be found in the configuration section. + +Example: + +.. code-block:: php + + = 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. + +Example: + +.. code-block:: php + + `. This +annotation is optional and only has meaning when used in +conjunction with @Id. + +If this annotation is not specified with @Id the NONE strategy is +used as default. + +Required attributes: + + +- **strategy**: Set the name of the identifier generation strategy. + Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE. + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It allows to hint the SchemaTool to +generate a database index on the specified table columns. It only +has meaning in the SchemaTool schema generation context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Example: + +.. code-block:: php + + ` and +:ref:`@DiscriminatorColumn ` annotations. + +Examples: + +.. code-block:: php + + `, :ref:`@OneToOne ` fields +and in the Context of :ref:`@JoinTable ` nested inside +a @ManyToMany. This annotation is not required. If its not +specified the attributes *name* and *referencedColumnName* are +inferred from the table and primary key names. + +Required attributes: + + +- **name**: Column name that holds the foreign key identifier for + this relation. In the context of @JoinTable it specifies the column + name in the join table. +- **referencedColumnName**: Name of the primary key identifier that + is used for joining of this relation. + +Optional attributes: + + +- **unique**: Determines if this relation exclusive between the + affected entities and should be enforced so on the database + constraint level. Defaults to false. +- **nullable**: Determine if the related entity is required, or if + null is an allowed state for the relation. Defaults to true. +- **onDelete**: Cascade Action (Database-level) +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute allows to make use of advanced RMDBS features. Using + this attribute on @JoinColumn is necessary if you need slightly + different column definitions for joining columns, for example + regarding NULL/NOT NULL defaults. However by default a + "columnDefinition" attribute on :ref:`@Column ` also sets + the related @JoinColumn's columnDefinition. This is necessary to + make foreign keys work. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToOne ` +relation with an entity that has multiple identifiers. + +.. _annref_jointable: + +@JoinTable +~~~~~~~~~~~~~~ + +Using :ref:`@OneToMany ` or +:ref:`@ManyToMany ` on the owning side of the relation +requires to specify the @JoinTable annotation which describes the +details of the database join table. If you do not specify +@JoinTable on these relations reasonable mapping defaults apply +using the affected table and the column names. + +Required attributes: + + +- **name**: Database name of the join-table +- **joinColumns**: An array of @JoinColumn annotations describing the + join-relation between the owning entities table and the join table. +- **inverseJoinColumns**: An array of @JoinColumn annotations + describing the join-relation between the inverse entities table and + the join table. + +Example: + +.. code-block:: php + + ` is an +additional, optional annotation that has reasonable default +configuration values using the table and names of the two related +entities. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **mappedBy**: This option specifies the property name on the + targetEntity that is the owning side of this relation. Its a + required attribute for the inverse side of a relationship. +- **inversedBy**: The inversedBy attribute designates the ï¬eld in the + entity that is the inverse side of the relationship. +- **cascade**: Cascade Option +- **fetch**: One of LAZY, EXTRA_LAZY or EAGER +- **indexBy**: Index the collection by a field on the target entity. + +.. note:: + + For ManyToMany bidirectional relationships either side may + be the owning side (the side that defines the @JoinTable and/or + does not make use of the mappedBy attribute, thus using a default + join table). + +Example: + +.. code-block:: php + + `. + +Optional attributes: + + +- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository. + That will be inherited for all subclasses of that Mapped Superclass. + +Example: + +.. code-block:: php + + ` with one additional option that can +be specified. The configuration defaults for +:ref:`@JoinColumn ` using the target entity table and +primary key column names apply here too. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **cascade**: Cascade Option +- **fetch**: One of LAZY or EAGER +- **orphanRemoval**: Boolean that specifies if orphans, inverse + OneToOne entities that are not connected to any owning instance, + should be removed by Doctrine. Defaults to false. +- **inversedBy**: The inversedBy attribute designates the ï¬eld in the + entity that is the inverse side of the relationship. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToMany ` +annotation to specify by which criteria the collection should be +retrieved from the database by using an ORDER BY clause. + +This annotation requires a single non-attributed value with an DQL +snippet: + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It allows to hint the SchemaTool to +generate a database unique constraint on the specified table +columns. It only has meaning in the SchemaTool schema generation +context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Example: + +.. code-block:: php + + ` annotations that have the type integer or +datetime. Combining @Version with :ref:`@Id ` is not supported. + +Example: + +.. code-block:: php + + `. +- An entity class must not implement ``__wakeup`` or + :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + Also consider implementing + `Serializable `_ + instead. +- Any two entity classes in a class hierarchy that inherit + directly or indirectly from one another must not have a mapped + property with the same name. That is, if B inherits from A then B + must not have a mapped field with the same name as an already + mapped field that is inherited from A. +- An entity cannot make use of func_get_args() to implement variable parameters. + Generated proxies do not support this for performance reasons and your code might + actually fail to work when violating this restriction. + +Entities support inheritance, polymorphic associations, and +polymorphic queries. Both abstract and concrete classes can be +entities. Entities may extend non-entity classes as well as entity +classes, and non-entity classes may extend entity classes. + +.. note:: + + The constructor of an entity is only ever invoked when + *you* construct a new instance with the *new* keyword. Doctrine + never calls entity constructors, thus you are free to use them as + you wish and even have it require arguments of any type. + + +Entity states +~~~~~~~~~~~~~ + +An entity instance can be characterized as being NEW, MANAGED, +DETACHED or REMOVED. + + +- A NEW entity instance has no persistent identity, and is not yet + associated with an EntityManager and a UnitOfWork (i.e. those just + created with the "new" operator). +- A MANAGED entity instance is an instance with a persistent + identity that is associated with an EntityManager and whose + persistence is thus managed. +- A DETACHED entity instance is an instance with a persistent + identity that is not (or no longer) associated with an + EntityManager and a UnitOfWork. +- A REMOVED entity instance is an instance with a persistent + identity, associated with an EntityManager, that will be removed + from the database upon transaction commit. + +.. _architecture_persistent_fields: + +Persistent fields +~~~~~~~~~~~~~~~~~ + +The persistent state of an entity is represented by instance +variables. An instance variable must be directly accessed only from +within the methods of the entity by the entity instance itself. +Instance variables must not be accessed by clients of the entity. +The state of the entity is available to clients only through the +entity’s methods, i.e. accessor methods (getter/setter methods) or +other business methods. + +Collection-valued persistent fields and properties must be defined +in terms of the ``Doctrine\Common\Collections\Collection`` +interface. The collection implementation type may be used by the +application to initialize fields or properties before the entity is +made persistent. Once the entity becomes managed (or detached), +subsequent access must be through the interface type. + +Serializing entities +~~~~~~~~~~~~~~~~~~~~ + +Serializing entities can be problematic and is not really +recommended, at least not as long as an entity instance still holds +references to proxy objects or is still managed by an +EntityManager. If you intend to serialize (and unserialize) entity +instances that still hold references to proxy objects you may run +into problems with private properties because of technical +limitations. Proxy objects implement ``__sleep`` and it is not +possible for ``__sleep`` to return names of private properties in +parent classes. On the other hand it is not a solution for proxy +objects to implement ``Serializable`` because Serializable does not +work well with any potential cyclic object references (at least we +did not find a way yet, if you did, please contact us). + +The EntityManager +~~~~~~~~~~~~~~~~~ + +The ``EntityManager`` class is a central access point to the ORM +functionality provided by Doctrine 2. The ``EntityManager`` API is +used to manage the persistence of your objects and to query for +persistent objects. + +Transactional write-behind +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An ``EntityManager`` and the underlying ``UnitOfWork`` employ a +strategy called "transactional write-behind" that delays the +execution of SQL statements in order to execute them in the most +efficient way and to execute them at the end of a transaction so +that all write locks are quickly released. You should see Doctrine +as a tool to synchronize your in-memory objects with the database +in well defined units of work. Work with your objects and modify +them as usual and when you're done call ``EntityManager#flush()`` +to make your changes persistent. + +The Unit of Work +~~~~~~~~~~~~~~~~ + +Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a +typical implementation of the +`Unit of Work pattern `_, +to keep track of all the things that need to be done the next time +``flush`` is invoked. You usually do not directly interact with a +``UnitOfWork`` but with the ``EntityManager`` instead. + + diff --git a/vendor/doctrine/orm/docs/en/reference/association-mapping.rst b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst new file mode 100644 index 00000000..f41bb610 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst @@ -0,0 +1,1145 @@ +Association Mapping +=================== + +This chapter introduces association mappings which are used to explain +references between objects and are mapped to a relational database using +foreign keys. + +Instead of working with the foreign keys directly you will always work with +references to objects: + +- A reference to a single object is represented by a foreign key. +- A collection of objects is represented by many foreign keys pointing to the object holding the collection + +This chapter is split into three different sections. + +- A list of all the possible association mapping use-cases is given. +- :ref:`association_mapping_defaults` are explained that simplify the use-case examples. +- :ref:`collections` are introduced that contain entities in associations. + +To master associations you should also learn about :doc:`owning and inverse sides of associations ` + +One-To-One, Unidirectional +-------------------------- + +A unidirectional one-to-one association is very common. Here is an +example of a ``Product`` that has one ``Shipping`` object +associated to it. The ``Shipping`` side does not reference back to +the ``Product`` so it is unidirectional. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + shipping_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id), + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Shipping ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Product ADD FOREIGN KEY (shipping_id) REFERENCES Shipping(id); + +One-To-One, Bidirectional +------------------------- + +Here is a one-to-one relationship between a ``Customer`` and a +``Cart``. The ``Cart`` has a reference back to the ``Customer`` so +it is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + .. code-block:: yaml + + Customer: + oneToOne: + cart: + targetEntity: Cart + mappedBy: customer + Cart: + oneToOne: + customer: + targetEntity: Customer + inversedBy: cart + joinColumn: + name: customer_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Cart ( + id INT AUTO_INCREMENT NOT NULL, + customer_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Customer ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id); + +See how the foreign key is defined on the owning side of the +relation, the table ``Cart``. + +One-To-One, Self-referencing +---------------------------- + +You can easily have self referencing one-to-one relationships like +below. + +.. code-block:: php + + phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Phonenumber + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + phonenumbers: + targetEntity: Phonenumber + joinTable: + name: users_phonenumbers + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + phonenumber_id: + referencedColumnName: id + unique: true + + +Generates the following MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE users_phonenumbers ( + user_id INT NOT NULL, + phonenumber_id INT NOT NULL, + UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id), + PRIMARY KEY(user_id, phonenumber_id) + ) ENGINE = InnoDB; + + CREATE TABLE Phonenumber ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id); + + +Many-To-One, Unidirectional +--------------------------- + +You can easily implement a many-to-one unidirectional association +with the following: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + + +.. note:: + + The above ``@JoinColumn`` is optional as it would default + to ``address_id`` and ``id`` anyways. You can omit it and let it + use the defaults. + + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + address_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE Address ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); + +One-To-Many, Bidirectional +-------------------------- + +Bidirectional one-to-many associations are very common. The +following code shows an example with a Product and a Feature +class: + +.. configuration-block:: + + .. code-block:: php + + features = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** @Entity **/ + class Feature + { + // ... + /** + * @ManyToOne(targetEntity="Product", inversedBy="features") + * @JoinColumn(name="product_id", referencedColumnName="id") + **/ + private $product; + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToMany: + features: + targetEntity: Feature + mappedBy: product + Feature: + type: entity + manyToOne: + product: + targetEntity: Product + inversedBy: features + joinColumn: + name: product_id + referencedColumnName: id + + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Feature ( + id INT AUTO_INCREMENT NOT NULL, + product_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id); + +One-To-Many, Self-referencing +----------------------------- + +You can also setup a one-to-many association that is +self-referencing. In this example we setup a hierarchy of +``Category`` objects by creating a self referencing relationship. +This effectively models a hierarchy of categories and from the +database perspective is known as an adjacency list approach. + +.. configuration-block:: + + .. code-block:: php + + children = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + .. code-block:: xml + + + + + + + + + .. code-block:: yaml + + Category: + type: entity + oneToMany: + children: + targetEntity: Category + mappedBy: parent + manyToOne: + parent: + targetEntity: Category + inversedBy: children + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Category ( + id INT AUTO_INCREMENT NOT NULL, + parent_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id); + +Many-To-Many, Unidirectional +---------------------------- + +Real many-to-many associations are less common. The following +example shows a unidirectional association between User and Group +entities: + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** @Entity **/ + class Group + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE users_groups ( + user_id INT NOT NULL, + group_id INT NOT NULL, + PRIMARY KEY(user_id, group_id) + ) ENGINE = InnoDB; + CREATE TABLE Group ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE users_groups ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_groups ADD FOREIGN KEY (group_id) REFERENCES Group(id); + +.. note:: + + Why are many-to-many associations less common? Because + frequently you want to associate additional attributes with an + association, in which case you introduce an association class. + Consequently, the direct many-to-many association disappears and is + replaced by one-to-many/many-to-one associations between the 3 + participating classes. + +Many-To-Many, Bidirectional +--------------------------- + +Here is a similar many-to-many relationship as above except this +one is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Group + { + // ... + /** + * @ManyToMany(targetEntity="User", mappedBy="groups") + **/ + private $users; + + public function __construct() { + $this->users = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + inversedBy: users + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + + Group: + type: entity + manyToMany: + users: + targetEntity: User + mappedBy: groups + +The MySQL schema is exactly the same as for the Many-To-Many +uni-directional case above. + +Picking Owning and Inverse Side +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Many-To-Many associations you can chose which entity is the +owning and which the inverse side. There is a very simple semantic +rule to decide which side is more suitable to be the owning side +from a developers perspective. You only have to ask yourself, which +entity is responsible for the connection management and pick that +as the owning side. + +Take an example of two entities ``Article`` and ``Tag``. Whenever +you want to connect an Article to a Tag and vice-versa, it is +mostly the Article that is responsible for this relation. Whenever +you add a new article, you want to connect it with existing or new +tags. Your create Article form will probably support this notion +and allow to specify the tags directly. This is why you should pick +the Article as owning side, as it makes the code more +understandable: + +.. code-block:: php + + addArticle($this); // synchronously updating inverse side + $this->tags[] = $tag; + } + } + + class Tag + { + private $articles; + + public function addArticle(Article $article) + { + $this->articles[] = $article; + } + } + +This allows to group the tag adding on the ``Article`` side of the +association: + +.. code-block:: php + + addTag($tagA); + $article->addTag($tagB); + +Many-To-Many, Self-referencing +------------------------------ + +You can even have a self-referencing many-to-many association. A +common scenario is where a ``User`` has friends and the target +entity of that relationship is a ``User`` so it is self +referencing. In this example it is bidirectional so ``User`` has a +field named ``$friendsWithMe`` and ``$myFriends``. + +.. code-block:: php + + friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection(); + $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE friends ( + user_id INT NOT NULL, + friend_user_id INT NOT NULL, + PRIMARY KEY(user_id, friend_user_id) + ) ENGINE = InnoDB; + ALTER TABLE friends ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE friends ADD FOREIGN KEY (friend_user_id) REFERENCES User(id); + +.. _association_mapping_defaults: + +Mapping Defaults +---------------- + +Before we introduce all the association mappings in detail, you +should note that the @JoinColumn and @JoinTable definitions are +usually optional and have sensible default values. The defaults for +a join column in a one-to-one/many-to-one association is as +follows: + +:: + + name: "_id" + referencedColumnName: "id" + +As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + +This is essentially the same as the following, more verbose, +mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +The @JoinTable definition used for many-to-many mappings has +similar defaults. As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + +This is essentially the same as the following, more verbose, +mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: User_Group + joinColumns: + User_id: + referencedColumnName: id + inverseJoinColumns: + Group_id: + referencedColumnName: id + +In that case, the name of the join table defaults to a combination +of the simple, unqualified class names of the participating +classes, separated by an underscore character. The names of the +join columns default to the simple, unqualified class name of the +targeted class followed by "\_id". The referencedColumnName always +defaults to "id", just as in one-to-one or many-to-one mappings. + +If you accept these defaults, you can reduce the mapping code to a +minimum. + +.. _collections: + +Collections +----------- + +In all the examples of many-valued associations in this manual we +will make use of a ``Collection`` interface and a corresponding +default implementation ``ArrayCollection`` that are defined in the +``Doctrine\Common\Collections`` namespace. Why do we need that? +Doesn't that couple my domain model to Doctrine? Unfortunately, PHP +arrays, while being great for many things, do not make up for good +collections of business objects, especially not in the context of +an ORM. The reason is that plain PHP arrays can not be +transparently extended / instrumented in PHP code, which is +necessary for a lot of advanced ORM features. The classes / +interfaces that come closest to an OO collection are ArrayAccess +and ArrayObject but until instances of these types can be used in +all places where a plain array can be used (something that may +happen in PHP6) their usability is fairly limited. You "can" +type-hint on ``ArrayAccess`` instead of ``Collection``, since the +Collection interface extends ``ArrayAccess``, but this will +severely limit you in the way you can work with the collection, +because the ``ArrayAccess`` API is (intentionally) very primitive +and more importantly because you can not pass this collection to +all the useful PHP array functions, which makes it very hard to +work with. + +.. warning:: + + The Collection interface and ArrayCollection class, + like everything else in the Doctrine namespace, are neither part of + the ORM, nor the DBAL, it is a plain PHP class that has no outside + dependencies apart from dependencies on PHP itself (and the SPL). + Therefore using this class in your domain classes and elsewhere + does not introduce a coupling to the persistence layer. The + Collection class, like everything else in the Common namespace, is + not part of the persistence layer. You could even copy that class + over to your project if you want to remove Doctrine from your + project and all your domain classes will work the same as before. + + + +Initializing Collections +------------------------ + +You have to be careful when using entity fields that contain a +collection of related entities. Say we have a User entity that +contains a collection of groups: + +.. code-block:: php + + groups; + } + } + +With this code alone the ``$groups`` field only contains an +instance of ``Doctrine\Common\Collections\Collection`` if the user +is retrieved from Doctrine, however not after you instantiated a +fresh instance of the User. When your user entity is still new +``$groups`` will obviously be null. + +This is why we recommend to initialize all collection fields to an +empty ``ArrayCollection`` in your entities constructor: + +.. code-block:: php + + groups = new ArrayCollection(); + } + + public function getGroups() + { + return $this->groups; + } + } + +Now the following code will work even if the Entity hasn't +been associated with an EntityManager yet: + +.. code-block:: php + + find('Group', $groupId); + $user = new User(); + $user->getGroups()->add($group); + diff --git a/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst new file mode 100644 index 00000000..2909048d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst @@ -0,0 +1,683 @@ +Basic Mapping +============= + +This chapter explains the basic mapping of objects and properties. +Mapping of associations will be covered in the next chapter +"Association Mapping". + +Mapping Drivers +--------------- + +Doctrine provides several different ways for specifying +object-relational mapping metadata: + + +- Docblock Annotations +- XML +- YAML + +This manual usually mentions docblock annotations in all the examples +that are spread throughout all chapters, however for many examples +alternative YAML and XML examples are given as well. There are dedicated +reference chapters for XML and YAML mapping, respectively that explain them +in more detail. There is also an Annotation reference chapter. + +.. note:: + + If you're wondering which mapping driver gives the best + performance, the answer is: They all give exactly the same performance. + Once the metadata of a class has + been read from the source (annotations, xml or yaml) it is stored + in an instance of the ``Doctrine\ORM\Mapping\ClassMetadata`` class + and these instances are stored in the metadata cache. Therefore at + the end of the day all drivers perform equally well. If you're not + using a metadata cache (not recommended!) then the XML driver might + have a slight edge in performance due to the powerful native XML + support in PHP. + + +Introduction to Docblock Annotations +------------------------------------ + +You've probably used docblock annotations in some form already, +most likely to provide documentation metadata for a tool like +``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a +tool to embed metadata inside the documentation section which can +then be processed by some tool. Doctrine 2 generalizes the concept +of docblock annotations so that they can be used for any kind of +metadata and so that it is easy to define new docblock annotations. +In order to allow more involved annotation values and to reduce the +chances of clashes with other docblock annotations, the Doctrine 2 +docblock annotations feature an alternative syntax that is heavily +inspired by the Annotation syntax introduced in Java 5. + +The implementation of these enhanced docblock annotations is +located in the ``Doctrine\Common\Annotations`` namespace and +therefore part of the Common package. Doctrine 2 docblock +annotations support namespaces and nested annotations among other +things. The Doctrine 2 ORM defines its own set of docblock +annotations for supplying object-relational mapping metadata. + +.. note:: + + If you're not comfortable with the concept of docblock + annotations, don't worry, as mentioned earlier Doctrine 2 provides + XML and YAML alternatives and you could easily implement your own + favourite mechanism for defining ORM metadata. + + +Persistent classes +------------------ + +In order to mark a class for object-relational persistence it needs +to be designated as an entity. This can be done through the +``@Entity`` marker annotation. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + # ... + +By default, the entity will be persisted to a table with the same +name as the class name. In order to change that, you can use the +``@Table`` annotation as follows: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + table: my_persistent_class + # ... + +Now instances of MyPersistentClass will be persisted into a table +named ``my_persistent_class``. + +Doctrine Mapping Types +---------------------- + +A Doctrine Mapping Type defines the mapping between a PHP type and +a SQL type. All Doctrine Mapping Types that ship with Doctrine are +fully portable between different RDBMS. You can even write your own +custom mapping types that might or might not be portable, which is +explained later in this chapter. + +For example, the Doctrine Mapping Type ``string`` defines the +mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. +depending on the RDBMS brand). Here is a quick overview of the +built-in mapping types: + + +- ``string``: Type that maps a SQL VARCHAR to a PHP string. +- ``integer``: Type that maps a SQL INT to a PHP integer. +- ``smallint``: Type that maps a database SMALLINT to a PHP + integer. +- ``bigint``: Type that maps a database BIGINT to a PHP string. +- ``boolean``: Type that maps a SQL boolean to a PHP boolean. +- ``decimal``: Type that maps a SQL DECIMAL to a PHP string. +- ``date``: Type that maps a SQL DATETIME to a PHP DateTime + object. +- ``time``: Type that maps a SQL TIME to a PHP DateTime object. +- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object. +- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object with timezone. +- ``text``: Type that maps a SQL CLOB to a PHP string. +- ``object``: Type that maps a SQL CLOB to a PHP object using + ``serialize()`` and ``unserialize()`` +- ``array``: Type that maps a SQL CLOB to a PHP array using + ``serialize()`` and ``unserialize()`` +- ``simple_array``: Type that maps a SQL CLOB to a PHP array using + ``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT* + Only use this type if you are sure that your values cannot contain a ",". +- ``json_array``: Type that maps a SQL CLOB to a PHP array using + ``json_encode()`` and ``json_decode()`` +- ``float``: Type that maps a SQL Float (Double Precision) to a + PHP double. *IMPORTANT*: Works only with locale settings that use + decimal points as separator. +- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to + varchar but uses a specific type if the platform supports it. +- ``blob``: Type that maps a SQL BLOB to a PHP resource stream + +.. note:: + + Doctrine Mapping Types are NOT SQL types and NOT PHP + types! They are mapping types between 2 types. + Additionally Mapping types are *case-sensitive*. For example, using + a DateTime column will NOT match the datetime type that ships with + Doctrine 2. + +.. note:: + + DateTime and Object types are compared by reference, not by value. Doctrine updates this values + if the reference changes and therefore behaves as if these objects are immutable value objects. + +.. warning:: + + All Date types assume that you are exclusively using the default timezone + set by `date_default_timezone_set() `_ + or by the php.ini configuration ``date.timezone``. Working with + different timezones will cause troubles and unexpected behavior. + + If you need specific timezone handling you have to handle this + in your domain, converting all the values back and forth from UTC. + There is also a :doc:`cookbook entry <../cookbook/working-with-datetime>` + on working with datetimes that gives hints for implementing + multi timezone applications. + + +Property Mapping +---------------- + +After a class has been marked as an entity it can specify mappings +for its instance fields. Here we will only look at simple fields +that hold scalar values like strings, numbers, etc. Associations to +other objects are covered in the chapter "Association Mapping". + +To mark a property for relational persistence the ``@Column`` +docblock annotation is used. This annotation usually requires at +least 1 attribute to be set, the ``type``. The ``type`` attribute +specifies the Doctrine Mapping Type to use for the field. If the +type is not specified, 'string' is used as the default mapping type +since it is the most flexible. + +Example: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + fields: + id: + type: integer + name: + length: 50 + +In that example we mapped the field ``id`` to the column ``id`` +using the mapping type ``integer`` and the field ``name`` is mapped +to the column ``name`` with the default mapping type ``string``. As +you can see, by default the column names are assumed to be the same +as the field names. To specify a different name for the column, you +can use the ``name`` attribute of the Column annotation as +follows: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + fields: + name: + length: 50 + column: db_name + +The Column annotation has some more attributes. Here is a complete +list: + + +- ``type``: (optional, defaults to 'string') The mapping type to + use for the column. +- ``column``: (optional, defaults to field name) The name of the + column in the database. +- ``length``: (optional, default 255) The length of the column in + the database. (Applies only if a string-valued column is used). +- ``unique``: (optional, default FALSE) Whether the column is a + unique key. +- ``nullable``: (optional, default FALSE) Whether the database + column is nullable. +- ``precision``: (optional, default 0) The precision for a decimal + (exact numeric) column. (Applies only if a decimal column is used.) +- ``scale``: (optional, default 0) The scale for a decimal (exact + numeric) column. (Applies only if a decimal column is used.) + +.. _reference-basic-mapping-custom-mapping-types: + +Custom Mapping Types +-------------------- + +Doctrine allows you to create new mapping types. This can come in +handy when you're missing a specific mapping type or when you want +to replace the existing implementation of a mapping type. + +In order to create a new mapping type you need to subclass +``Doctrine\DBAL\Types\Type`` and implement/override the methods as +you wish. Here is an example skeleton of such a custom type class: + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); + +Now using Schema-Tool, whenever it detects a column having the +``db_mytype`` it will convert it into a ``mytype`` Doctrine Type +instance for Schema representation. Keep in mind that you can +easily produce clashes this way, each database type can only map to +exactly one Doctrine mapping type. + +Custom ColumnDefinition +----------------------- + +You can define a custom definition for each column using the "columnDefinition" +attribute of ``@Column``. You have to define all the definitions that follow +the name of a column here. + +.. note:: + + Using columnDefinition will break change-detection in SchemaTool. + +Identifiers / Primary Keys +-------------------------- + +Every entity class needs an identifier/primary key. You designate +the field that serves as the identifier with the ``@Id`` marker +annotation. Here is an example: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + id: + id: + type: integer + fields: + name: + length: 50 + +Without doing anything else, the identifier is assumed to be +manually assigned. That means your code would need to properly set +the identifier property before passing a new entity to +``EntityManager#persist($entity)``. + +A common alternative strategy is to use a generated value as the +identifier. To do this, you use the ``@GeneratedValue`` annotation +like this: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + length: 50 + +This tells Doctrine to automatically generate a value for the +identifier. How this value is generated is specified by the +``strategy`` attribute, which is optional and defaults to 'AUTO'. A +value of ``AUTO`` tells Doctrine to use the generation strategy +that is preferred by the currently used database platform. See +below for details. + +Identifier Generation Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The previous example showed how to use the default identifier +generation strategy without knowing the underlying database with +the AUTO-detection strategy. It is also possible to specify the +identifier generation strategy more explicitly, which allows to +make use of some additional features. + +Here is the list of possible generation strategies: + + +- ``AUTO`` (default): Tells Doctrine to pick the strategy that is + preferred by the used database platform. The preferred strategies + are IDENTITY for MySQL, SQLite and MsSQL and SEQUENCE for Oracle + and PostgreSQL. This strategy provides full portability. +- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID + generation. This strategy does currently not provide full + portability. Sequences are supported by Oracle and PostgreSql. +- ``IDENTITY``: Tells Doctrine to use special identity columns in + the database that generate a value on insertion of a row. This + strategy does currently not provide full portability and is + supported by the following platforms: MySQL/SQLite + (AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL). +- ``TABLE``: Tells Doctrine to use a separate table for ID + generation. This strategy provides full portability. + ***This strategy is not yet implemented!*** +- ``NONE``: Tells Doctrine that the identifiers are assigned (and + thus generated) by your code. The assignment must take place before + a new entity is passed to ``EntityManager#persist``. NONE is the + same as leaving off the @GeneratedValue entirely. + +Sequence Generator +^^^^^^^^^^^^^^^^^^ + +The Sequence Generator can currently be used in conjunction with +Oracle or Postgres and allows some additional configuration options +besides specifying the sequence's name: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + MyPersistentClass: + type: entity + id: + id: + type: integer + generator: + strategy: SEQUENCE + sequenceGenerator: + sequenceName: tablename_seq + allocationSize: 100 + initialValue: 1 + +The initial value specifies at which value the sequence should +start. + +The allocationSize is a powerful feature to optimize INSERT +performance of Doctrine. The allocationSize specifies by how much +values the sequence is incremented whenever the next value is +retrieved. If this is larger than 1 (one) Doctrine can generate +identifier values for the allocationSizes amount of entities. In +the above example with ``allocationSize=100`` Doctrine 2 would only +need to access the sequence once to generate the identifiers for +100 new entities. + +*The default allocationSize for a @SequenceGenerator is currently 10.* + +.. caution:: + + The allocationSize is detected by SchemaTool and + transformed into an "INCREMENT BY " clause in the CREATE SEQUENCE + statement. For a database schema created manually (and not + SchemaTool) you have to make sure that the allocationSize + configuration option is never larger than the actual sequences + INCREMENT BY value, otherwise you may get duplicate keys. + + +.. note:: + + It is possible to use strategy="AUTO" and at the same time + specifying a @SequenceGenerator. In such a case, your custom + sequence settings are used in the case where the preferred strategy + of the underlying platform is SEQUENCE, such as for Oracle and + PostgreSQL. + + +Composite Keys +~~~~~~~~~~~~~~ + +Doctrine 2 allows to use composite primary keys. There are however +some restrictions opposed to using a single identifier. The use of +the ``@GeneratedValue`` annotation is only supported for simple +(not composite) primary keys, which means you can only use +composite keys if you generate the primary key values yourself +before calling ``EntityManager#persist()`` on the entity. + +To designate a composite primary key / identifier, simply put the +@Id marker annotation on all fields that make up the primary key. + +Quoting Reserved Words +---------------------- + +It may sometimes be necessary to quote a column or table name +because it conflicts with a reserved word of the particular RDBMS +in use. This is often referred to as "Identifier Quoting". To let +Doctrine know that you would like a table or column name to be +quoted in all SQL statements, enclose the table or column name in +backticks. Here is an example: + +.. code-block:: php + + setStatus('user'); + $user->setUsername('user' . $i); + $user->setName('Mr.Smith-' . $i); + $em->persist($user); + if (($i % $batchSize) === 0) { + $em->flush(); + $em->clear(); // Detaches all objects from Doctrine! + } + } + +Bulk Updates +------------ + +There are 2 possibilities for bulk updates with Doctrine. + +DQL UPDATE +~~~~~~~~~~ + +The by far most efficient way for bulk updates is to use a DQL +UPDATE query. Example: + +.. code-block:: php + + createQuery('update MyProject\Model\Manager m set m.salary = m.salary * 0.9'); + $numUpdated = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk updates is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this, combining the iteration +with the batching strategy that was already used for bulk inserts: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach($iterableResult AS $row) { + $user = $row[0]; + $user->increaseCredit(); + $user->calculateNewBonuses(); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + +Bulk Deletes +------------ + +There are two possibilities for bulk deletes with Doctrine. You can +either issue a single DQL DELETE query or you can iterate over +results removing them one at a time. + +DQL DELETE +~~~~~~~~~~ + +The by far most efficient way for bulk deletes is to use a DQL +DELETE query. + +Example: + +.. code-block:: php + + createQuery('delete from MyProject\Model\Manager m where m.salary > 100000'); + $numDeleted = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk deletes is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + while (($row = $iterableResult->next()) !== false) { + $em->remove($row[0]); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all deletions. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + +Iterating Large Results for Data-Processing +------------------------------------------- + +You can use the ``iterate()`` method just to iterate over a large +result and no UPDATE or DELETE intention. The ``IterableResult`` +instance returned from ``$query->iterate()`` implements the +Iterator interface so you can process a large result without memory +problems using the following approach: + +.. code-block:: php + + _em->createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach ($iterableResult AS $row) { + // do stuff with the data in the row, $row[0] is always the object + + // detach from Doctrine, so that it can be Garbage-Collected immediately + $this->_em->detach($row[0]); + } + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + + diff --git a/vendor/doctrine/orm/docs/en/reference/best-practices.rst b/vendor/doctrine/orm/docs/en/reference/best-practices.rst new file mode 100644 index 00000000..f58291d1 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/best-practices.rst @@ -0,0 +1,127 @@ +Best Practices +============== + +The best practices mentioned here that affect database +design generally refer to best practices when working with Doctrine +and do not necessarily reflect best practices for database design +in general. + + +Don't use public properties on entities +--------------------------------------- + +It is very important that you don't map public properties on +entities, but only protected or private ones. The reason for this +is simple, whenever you access a public property of a proxy object +that hasn't been initialized yet the return value will be null. +Doctrine cannot hook into this process and magically make the +entity lazy load. + +This can create situations where it is very hard to debug the +current failure. We therefore urge you to map only private and +protected properties on entities and use getter methods or magic +\_\_get() to access them. + +Constrain relationships as much as possible +------------------------------------------- + +It is important to constrain relationships as much as possible. +This means: + + +- Impose a traversal direction (avoid bidirectional associations + if possible) +- Eliminate nonessential associations + +This has several benefits: + + +- Reduced coupling in your domain model +- Simpler code in your domain model (no need to maintain + bidirectionality properly) +- Less work for Doctrine + +Avoid composite keys +-------------------- + +Even though Doctrine fully supports composite keys it is best not +to use them if possible. Composite keys require additional work by +Doctrine and thus have a higher probability of errors. + +Use events judiciously +---------------------- + +The event system of Doctrine is great and fast. Even though making +heavy use of events, especially lifecycle events, can have a +negative impact on the performance of your application. Thus you +should use events judiciously. + +Use cascades judiciously +------------------------ + +Automatic cascades of the persist/remove/merge/etc. operations are +very handy but should be used wisely. Do NOT simply add all +cascades to all associations. Think about which cascades actually +do make sense for you for a particular association, given the +scenarios it is most likely used in. + +Don't use special characters +---------------------------- + +Avoid using any non-ASCII characters in class, field, table or +column names. Doctrine itself is not unicode-safe in many places +and will not be until PHP itself is fully unicode-aware (PHP6). + +Don't use identifier quoting +---------------------------- + +Identifier quoting is a workaround for using reserved words that +often causes problems in edge cases. Do not use identifier quoting +and avoid using reserved words as table or column names. + +Initialize collections in the constructor +----------------------------------------- + +It is recommended best practice to initialize any business +collections in entities in the constructor. Example: + +.. code-block:: php + + addresses = new ArrayCollection; + $this->articles = new ArrayCollection; + } + } + +Don't map foreign keys to fields in an entity +--------------------------------------------- + +Foreign keys have no meaning whatsoever in an object model. Foreign +keys are how a relational database establishes relationships. Your +object model establishes relationships through object references. +Thus mapping foreign keys to object fields heavily leaks details of +the relational model into the object model, something you really +should not do. + +Use explicit transaction demarcation +------------------------------------ + +While Doctrine will automatically wrap all DML operations in a +transaction on flush(), it is considered best practice to +explicitly set the transaction boundaries yourself. Otherwise every +single query is wrapped in a small transaction (Yes, SELECT +queries, too) since you can not talk to your database outside of a +transaction. While such short transactions for read-only (SELECT) +queries generally don't have any noticeable performance impact, it +is still preferable to use fewer, well-defined transactions that +are established through explicit transaction boundaries. + + diff --git a/vendor/doctrine/orm/docs/en/reference/caching.rst b/vendor/doctrine/orm/docs/en/reference/caching.rst new file mode 100644 index 00000000..1e87efaf --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/caching.rst @@ -0,0 +1,439 @@ +Caching +======= + +Doctrine provides cache drivers in the ``Common`` package for some +of the most popular caching implementations such as APC, Memcache +and Xcache. We also provide an ``ArrayCache`` driver which stores +the data in a PHP array. Obviously, the cache does not live between +requests but this is useful for testing in a development +environment. + +Cache Drivers +------------- + +The cache drivers follow a simple interface that is defined in +``Doctrine\Common\Cache\Cache``. All the cache drivers extend a +base class ``Doctrine\Common\Cache\AbstractCache`` which implements +the before mentioned interface. + +The interface defines the following methods for you to publicly +use. + + +- fetch($id) - Fetches an entry from the cache. +- contains($id) - Test if an entry exists in the cache. +- save($id, $data, $lifeTime = false) - Puts data into the cache. +- delete($id) - Deletes a cache entry. + +Each driver extends the ``AbstractCache`` class which defines a few +abstract protected methods that each of the drivers must +implement. + + +- \_doFetch($id) +- \_doContains($id) +- \_doSave($id, $data, $lifeTime = false) +- \_doDelete($id) + +The public methods ``fetch()``, ``contains()``, etc. utilize the +above protected methods that are implemented by the drivers. The +code is organized this way so that the protected methods in the +drivers do the raw interaction with the cache implementation and +the ``AbstractCache`` can build custom functionality on top of +these methods. + +APC +~~~ + +In order to use the APC cache driver you must have it compiled and +enabled in your php.ini. You can read about APC +`in the PHP Documentation `_. It will give +you a little background information about what it is and how you +can use it as well as how to install it. + +Below is a simple example of how you could use the APC cache driver +by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Memcache +~~~~~~~~ + +In order to use the Memcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Memcache +` on the PHP website `_. It will +give you a little background information about what it is and how +you can use it as well as how to install it. + +Below is a simple example of how you could use the Memcache cache +driver by itself. + +.. code-block:: php + + connect('memcache_host', 11211); + + $cacheDriver = new \Doctrine\Common\Cache\MemcacheCache(); + $cacheDriver->setMemcache($memcache); + $cacheDriver->save('cache_id', 'my_data'); + +Xcache +~~~~~~ + +In order to use the Xcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Xcache +`here `_. It will give you a little +background information about what it is and how you can use it as +well as how to install it. + +Below is a simple example of how you could use the Xcache cache +driver by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Redis +~~~~~ + +In order to use the Redis cache driver you must have it compiled +and enabled in your php.ini. You can read about what is Redis +`from here `_. Also check +`here `_ for how you can use +and install Redis PHP extension. + +Below is a simple example of how you could use the Redis cache +driver by itself. + +.. code-block:: php + + connect('redis_host', 6379); + + $cacheDriver = new \Doctrine\Common\Cache\RedisCache(); + $cacheDriver->setRedis($redis); + $cacheDriver->save('cache_id', 'my_data'); + +Using Cache Drivers +------------------- + +In this section we'll describe how you can fully utilize the API of +the cache drivers to save cache, check if some cache exists, fetch +the cached data and delete the cached data. We'll use the +``ArrayCache`` implementation as our example here. + +.. code-block:: php + + save('cache_id', 'my_data'); + +The ``save()`` method accepts three arguments which are described +below. + + +- ``$id`` - The cache id +- ``$data`` - The cache entry/data. +- ``$lifeTime`` - The lifetime. If != false, sets a specific + lifetime for this cache entry (null => infinite lifeTime). + +You can save any type of data whether it be a string, array, +object, etc. + +.. code-block:: php + + 'value1', + 'key2' => 'value2' + ); + $cacheDriver->save('my_array', $array); + +Checking +~~~~~~~~ + +Checking whether some cache exists is very simple, just use the +``contains()`` method. It accepts a single argument which is the ID +of the cache entry. + +.. code-block:: php + + contains('cache_id')) { + echo 'cache exists'; + } else { + echo 'cache does not exist'; + } + +Fetching +~~~~~~~~ + +Now if you want to retrieve some cache entry you can use the +``fetch()`` method. It also accepts a single argument just like +``contains()`` which is the ID of the cache entry. + +.. code-block:: php + + fetch('my_array'); + +Deleting +~~~~~~~~ + +As you might guess, deleting is just as easy as saving, checking +and fetching. We have a few ways to delete cache entries. You can +delete by an individual ID, regular expression, prefix, suffix or +you can delete all entries. + +By Cache ID +^^^^^^^^^^^ + +.. code-block:: php + + delete('my_array'); + +All +^^^ + +If you simply want to delete all cache entries you can do so with +the ``deleteAll()`` method. + +.. code-block:: php + + deleteAll(); + +Namespaces +~~~~~~~~~~ + +If you heavily use caching in your application and utilize it in +multiple parts of your application, or use it in different +applications on the same server you may have issues with cache +naming collisions. This can be worked around by using namespaces. +You can set the namespace a cache driver should use by using the +``setNamespace()`` method. + +.. code-block:: php + + setNamespace('my_namespace_'); + +Integrating with the ORM +------------------------ + +The Doctrine ORM package is tightly integrated with the cache +drivers to allow you to improve performance of various aspects of +Doctrine by just simply making some additional configurations and +method calls. + +Query Cache +~~~~~~~~~~~ + +It is highly recommended that in a production environment you cache +the transformation of a DQL query to its SQL counterpart. It +doesn't make sense to do this parsing multiple times as it doesn't +change unless you alter the DQL query. + +This can be done by configuring the query cache implementation to +use on your ORM configuration. + +.. code-block:: php + + setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Result Cache +~~~~~~~~~~~~ + +The result cache can be used to cache the results of your queries +so that we don't have to query the database or hydrate the data +again after the first time. You just need to configure the result +cache implementation. + +.. code-block:: php + + setResultCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now when you're executing DQL queries you can configure them to use +the result cache. + +.. code-block:: php + + createQuery('select u from \Entities\User u'); + $query->useResultCache(true); + +You can also configure an individual query to use a different +result cache driver. + +.. code-block:: php + + setResultCacheDriver(new \Doctrine\Common\Cache\ApcCache()); + +.. note:: + + Setting the result cache driver on the query will + automatically enable the result cache for the query. If you want to + disable it pass false to ``useResultCache()``. + + :: + + useResultCache(false); + + +If you want to set the time the cache has to live you can use the +``setResultCacheLifetime()`` method. + +.. code-block:: php + + setResultCacheLifetime(3600); + +The ID used to store the result set cache is a hash which is +automatically generated for you if you don't set a custom ID +yourself with the ``setResultCacheId()`` method. + +.. code-block:: php + + setResultCacheId('my_custom_id'); + +You can also set the lifetime and cache ID by passing the values as +the second and third argument to ``useResultCache()``. + +.. code-block:: php + + useResultCache(true, 3600, 'my_custom_id'); + +Metadata Cache +~~~~~~~~~~~~~~ + +Your class metadata can be parsed from a few different sources like +YAML, XML, Annotations, etc. Instead of parsing this information on +each request we should cache it using one of the cache drivers. + +Just like the query and result cache we need to configure it +first. + +.. code-block:: php + + setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now the metadata information will only be parsed once and stored in +the cache driver. + +Clearing the Cache +------------------ + +We've already shown you previously how you can use the API of the +cache drivers to manually delete cache entries. For your +convenience we offer a command line task for you to help you with +clearing the query, result and metadata cache. + +From the Doctrine command line you can run the following command. + +.. code-block:: php + + $ ./doctrine clear-cache + +Running this task with no arguments will clear all the cache for +all the configured drivers. If you want to be more specific about +what you clear you can use the following options. + +To clear the query cache use the ``--query`` option. + +.. code-block:: php + + $ ./doctrine clear-cache --query + +To clear the metadata cache use the ``--metadata`` option. + +.. code-block:: php + + $ ./doctrine clear-cache --metadata + +To clear the result cache use the ``--result`` option. + +.. code-block:: php + + $ ./doctrine clear-cache --result + +When you use the ``--result`` option you can use some other options +to be more specific about what queries result sets you want to +clear. + +Just like the API of the cache drivers you can clear based on an +ID, regular expression, prefix or suffix. + +.. code-block:: php + + $ ./doctrine clear-cache --result --id=cache_id + +Or if you want to clear based on a regular expressions. + +.. code-block:: php + + $ ./doctrine clear-cache --result --regex=users_.* + +Or with a prefix. + +.. code-block:: php + + $ ./doctrine clear-cache --result --prefix=users_ + +And finally with a suffix. + +.. code-block:: php + + $ ./doctrine clear-cache --result --suffix=_my_account + +.. note:: + + Using the ``--id``, ``--regex``, etc. options with the + ``--query`` and ``--metadata`` are not allowed as it is not + necessary to be specific about what you clear. You only ever need + to completely clear the cache to remove stale entries. + + +Cache Slams +----------- + +Something to be careful of when utilizing the cache drivers is +cache slams. If you have a heavily trafficked website with some +code that checks for the existence of a cache record and if it does +not exist it generates the information and saves it to the cache. +Now if 100 requests were issued all at the same time and each one +sees the cache does not exist and they all try and insert the same +cache entry it could lock up APC, Xcache, etc. and cause problems. +Ways exist to work around this, like pre-populating your cache and +not letting your users requests populate the cache. + +You can read more about cache slams +`in this blog post `_. + + diff --git a/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst new file mode 100644 index 00000000..d0f09989 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst @@ -0,0 +1,151 @@ +Change Tracking Policies +======================== + +Change tracking is the process of determining what has changed in +managed entities since the last time they were synchronized with +the database. + +Doctrine provides 3 different change tracking policies, each having +its particular advantages and disadvantages. The change tracking +policy can be defined on a per-class basis (or more precisely, +per-hierarchy). + +Deferred Implicit +~~~~~~~~~~~~~~~~~ + +The deferred implicit policy is the default change tracking policy +and the most convenient one. With this policy, Doctrine detects the +changes by a property-by-property comparison at commit time and +also detects changes to entities or new entities that are +referenced by other managed entities ("persistence by +reachability"). Although the most convenient policy, it can have +negative effects on performance if you are dealing with large units +of work (see "Understanding the Unit of Work"). Since Doctrine +can't know what has changed, it needs to check all managed entities +for changes every time you invoke EntityManager#flush(), making +this operation rather costly. + +Deferred Explicit +~~~~~~~~~~~~~~~~~ + +The deferred explicit policy is similar to the deferred implicit +policy in that it detects changes through a property-by-property +comparison at commit time. The difference is that Doctrine 2 only +considers entities that have been explicitly marked for change detection +through a call to EntityManager#persist(entity) or through a save +cascade. All other entities are skipped. This policy therefore +gives improved performance for larger units of work while +sacrificing the behavior of "automatic dirty checking". + +Therefore, flush() operations are potentially cheaper with this +policy. The negative aspect this has is that if you have a rather +large application and you pass your objects through several layers +for processing purposes and business tasks you may need to track +yourself which entities have changed on the way so you can pass +them to EntityManager#persist(). + +This policy can be configured as follows: + +.. code-block:: php + + _listeners[] = $listener; + } + } + +Then, in each property setter of this class or derived classes, you +need to notify all the ``PropertyChangedListener`` instances. As an +example we add a convenience method on ``MyEntity`` that shows this +behaviour: + +.. code-block:: php + + _listeners) { + foreach ($this->_listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + + public function setData($data) + { + if ($data != $this->data) { + $this->_onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +You have to invoke ``_onPropertyChanged`` inside every method that +changes the persistent state of ``MyEntity``. + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you also have full control +over when you consider a property changed. + +The negative point of this policy is obvious: You need implement an +interface and write some plumbing code. But also note that we tried +hard to keep this notification functionality abstract. Strictly +speaking, it has nothing to do with the persistence layer and the +Doctrine ORM or DBAL. You may find that property notification +events come in handy in many other scenarios as well. As mentioned +earlier, the ``Doctrine\Common`` namespace is not that evil and +consists solely of very small classes and interfaces that have +almost no external dependencies (none to the DBAL and none to the +ORM) and that you can easily take with you should you want to swap +out the persistence layer. This change tracking policy does not +introduce a dependency on the Doctrine DBAL/ORM or the persistence +layer. + +The positive point and main advantage of this policy is its +effectiveness. It has the best performance characteristics of the 3 +policies with larger units of work and a flush() operation is very +cheap when nothing has changed. + + diff --git a/vendor/doctrine/orm/docs/en/reference/configuration.rst b/vendor/doctrine/orm/docs/en/reference/configuration.rst new file mode 100644 index 00000000..d8e718b5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/configuration.rst @@ -0,0 +1,140 @@ +Installation and Configuration +============================== + +Doctrine can be installed with `Composer `_. For +older versions we still have `PEAR packages +`_. + +Define the following requirement in your ``composer.json`` file: + +:: + + { + "require": { + "doctrine/orm": "*" + } + } + +Then call ``composer install`` from your command line. If you don't know +how Composer works, check out their `Getting Started +`_ to set up. + +Class loading +------------- + +Autoloading is taken care of by Composer. You just have to include the composer autoload file in your project: + +.. code-block:: php + + 'pdo_mysql', + 'user' => 'root', + 'password' => '', + 'dbname' => 'foo', + ); + + $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); + $entityManager = EntityManager::create($dbParams, $config); + +Or if you prefer XML: + +.. code-block:: php + + ` section. + +.. note:: + + You can learn more about the database connection configuration in the + `Doctrine DBAL connection configuration reference `_. + +Setting up the Commandline Tool +------------------------------- + +Doctrine ships with a number of command line tools that are very helpful +during development. You can call this command from the Composer binary +directory: + +.. code-block:: + + $ php vendor/bin/doctrine + +You need to register your applications EntityManager to the console tool +to make use of the tasks by creating a ``cli-config.php`` file with the +following content: + +On Doctrine 2.4 and above: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); diff --git a/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst new file mode 100644 index 00000000..856a4b07 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst @@ -0,0 +1,1672 @@ +Doctrine Query Language +=========================== + +DQL stands for Doctrine Query Language and is an Object +Query Language derivate that is very similar to the Hibernate +Query Language (HQL) or the Java Persistence Query Language (JPQL). + +In essence, DQL provides powerful querying capabilities over your +object model. Imagine all your objects lying around in some storage +(like an object database). When writing DQL queries, think about +querying that storage to pick a certain subset of your objects. + +.. note:: + + A common mistake for beginners is to mistake DQL for + being just some form of SQL and therefore trying to use table names + and column names or join arbitrary tables together in a query. You + need to think about DQL as a query language for your object model, + not for your relational schema. + + +DQL is case in-sensitive, except for namespace, class and field +names, which are case sensitive. + +Types of DQL queries +-------------------- + +DQL as a query language has SELECT, UPDATE and DELETE constructs +that map to their corresponding SQL statement types. INSERT +statements are not allowed in DQL, because entities and their +relations have to be introduced into the persistence context +through ``EntityManager#persist()`` to ensure consistency of your +object model. + +DQL SELECT statements are a very powerful way of retrieving parts +of your domain model that are not accessible via associations. +Additionally they allow to retrieve entities and their associations +in one single SQL select statement which can make a huge difference +in performance in contrast to using several queries. + +DQL UPDATE and DELETE statements offer a way to execute bulk +changes on the entities of your domain model. This is often +necessary when you cannot load all the affected entities of a bulk +update into memory. + +SELECT queries +-------------- + +DQL SELECT clause +~~~~~~~~~~~~~~~~~ + +The select clause of a DQL query specifies what appears in the +query result. The composition of all the expressions in the select +clause also influences the nature of the query result. + +Here is an example that selects all users with an age > 20: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.age > 20'); + $users = $query->getResult(); + +Lets examine the query: + + +- ``u`` is a so called identification variable or alias that + refers to the ``MyProject\Model\User`` class. By placing this alias + in the SELECT clause we specify that we want all instances of the + User class that are matched by this query to appear in the query + result. +- The FROM keyword is always followed by a fully-qualified class + name which in turn is followed by an identification variable or + alias for that class name. This class designates a root of our + query from which we can navigate further via joins (explained + later) and path expressions. +- The expression ``u.age`` in the WHERE clause is a path + expression. Path expressions in DQL are easily identified by the + use of the '.' operator that is used for constructing paths. The + path expression ``u.age`` refers to the ``age`` field on the User + class. + +The result of this query would be a list of User objects where all +users are older than 20. + +The SELECT clause allows to specify both class identification +variables that signal the hydration of a complete entity class or +just fields of the entity using the syntax ``u.name``. Combinations +of both are also allowed and it is possible to wrap both fields and +identification values into aggregation and DQL functions. Numerical +fields can be part of computations using mathematical operations. +See the sub-section on `Functions, Operators, Aggregates`_ for +more information. + +Joins +~~~~~ + +A SELECT query can contain joins. There are 2 types of JOINs: +"Regular" Joins and "Fetch" Joins. + +**Regular Joins**: Used to limit the results and/or compute +aggregate values. + +**Fetch Joins**: In addition to the uses of regular joins: Used to +fetch related entities and include them in the hydrated result of a +query. + +There is no special DQL keyword that distinguishes a regular join +from a fetch join. A join (be it an inner or outer join) becomes a +"fetch join" as soon as fields of the joined entity appear in the +SELECT part of the DQL query outside of an aggregate function. +Otherwise its a "regular join". + +Example: + +Regular join of the address: + +.. code-block:: php + + createQuery("SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +Fetch join of the address: + +.. code-block:: php + + createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +When Doctrine hydrates a query with fetch-join it returns the class +in the FROM clause on the root level of the result array. In the +previous example an array of User instances is returned and the +address of each user is fetched and hydrated into the +``User#address`` variable. If you access the address Doctrine does +not need to lazy load the association with another query. + +.. note:: + + Doctrine allows you to walk all the associations between + all the objects in your domain model. Objects that were not already + loaded from the database are replaced with lazy load proxy + instances. Non-loaded Collections are also replaced by lazy-load + instances that fetch all the contained objects upon first access. + However relying on the lazy-load mechanism leads to many small + queries executed against the database, which can significantly + affect the performance of your application. **Fetch Joins** are the + solution to hydrate most or all of the entities that you need in a + single SELECT query. + + +Named and Positional Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +DQL supports both named and positional parameters, however in +contrast to many SQL dialects positional parameters are specified +with numbers, for example "?1", "?2" and so on. Named parameters +are specified with ":name1", ":name2" and so on. + +When referencing the parameters in ``Query#setParameter($param, $value)`` +both named and positional parameters are used **without** their prefixes. + +DQL SELECT Examples +~~~~~~~~~~~~~~~~~~~ + +This section contains a large set of DQL queries and some +explanations of what is happening. The actual result also depends +on the hydration mode. + +Hydrate all User entities: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u'); + $users = $query->getResult(); // array of User objects + +Retrieve the IDs of all CmsUsers: + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve the IDs of all users that have written an article: + +.. code-block:: php + + createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve all articles and sort them by the name of the articles +users instance: + +.. code-block:: php + + createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC'); + $articles = $query->getResult(); // array of CmsArticle objects + +Retrieve the Username and Name of a CmsUser: + +.. code-block:: php + + createQuery('SELECT u.username, u.name FROM CmsUser u'); + $users = $query->getResult(); // array of CmsUser username and name values + echo $users[0]['username']; + +Retrieve a ForumUser and his single associated entity: + +.. code-block:: php + + createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a'); + $users = $query->getResult(); // array of ForumUser objects with the avatar association loaded + echo get_class($users[0]->getAvatar()); + +Retrieve a CmsUser and fetch join all the phonenumbers he has: + +.. code-block:: php + + createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p'); + $users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded + $phonenumbers = $users[0]->getPhonenumbers(); + +Hydrate a result in Ascending: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC'); + $users = $query->getResult(); // array of ForumUser objects + +Or in Descending Order: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC'); + $users = $query->getResult(); // array of ForumUser objects + +Using Aggregate Functions: + +.. code-block:: php + + createQuery('SELECT COUNT(u.id) FROM Entities\User u'); + $count = $query->getSingleScalarResult(); + + $query = $em->createQuery('SELECT u, count(g.id) FROM Entities\User u JOIN u.groups g GROUP BY u.id'); + $result = $query->getResult(); + +With WHERE Clause and Positional Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $users = $query->getResult(); // array of ForumUser objects + +With WHERE Clause and Named Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.username = :name'); + $query->setParameter('name', 'Bob'); + $users = $query->getResult(); // array of ForumUser objects + +With Nested Conditions in WHERE Clause: + +.. code-block:: php + + createQuery('SELECT u from ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id'); + $query->setParameters(array( + 'name' => 'Bob', + 'name2' => 'Alice', + 'id' => 321, + )); + $users = $query->getResult(); // array of ForumUser objects + +With COUNT DISTINCT: + +.. code-block:: php + + createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser'); + $users = $query->getResult(); // array of ForumUser objects + +With Arithmetic Expression in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); + $users = $query->getResult(); // array of ForumUser objects + +Using a LEFT JOIN to hydrate all user-ids and optionally associated +article-ids: + +.. code-block:: php + + createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a'); + $results = $query->getResult(); // array of user ids and every article_id for each user + +Restricting a JOIN clause by additional conditions: + +.. code-block:: php + + createQuery("SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE :foo"); + $query->setParameter('foo', '%foo%'); + $users = $query->getResult(); + +Using several Fetch JOINs: + +.. code-block:: php + + createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c'); + $users = $query->getResult(); + +BETWEEN in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2'); + $query->setParameter(1, 123); + $query->setParameter(2, 321); + $usernames = $query->getResult(); + +DQL Functions in WHERE clause: + +.. code-block:: php + + createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'"); + $usernames = $query->getResult(); + +IN() Expression: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id IN(46)'); + $usernames = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id IN (1, 2)'); + $users = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)'); + $users = $query->getResult(); + +CONCAT() DQL Function: + +.. code-block:: php + + createQuery("SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1"); + $query->setParameter(1, 'Jess'); + $ids = $query->getResult(); + + $query = $em->createQuery('SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $idUsernames = $query->getResult(); + +EXISTS in WHERE clause with correlated Subquery + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)'); + $ids = $query->getResult(); + +Get all users who are members of $group. + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups'); + $query->setParameter('groupId', $group); + $ids = $query->getResult(); + +Get all users that have more than 1 phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1'); + $users = $query->getResult(); + +Get all users that have no phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY'); + $users = $query->getResult(); + +Get all instances of a specific type, for use with inheritance +hierarchies: + +.. versionadded:: 2.1 + +.. code-block:: php + + createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1'); + +Get all users visible on a given website that have chosen certain gender: + +.. versionadded:: 2.2 + +.. code-block:: php + + createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)'); + +Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys: + +.. code-block:: php + + createQuery('SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1'); + + +Partial Object Syntax +^^^^^^^^^^^^^^^^^^^^^ + +By default when you run a DQL query in Doctrine and select only a +subset of the fields for a given entity, you do not receive objects +back. Instead, you receive only arrays as a flat rectangular result +set, similar to how you would if you were just using SQL directly +and joining some data. + +If you want to select partial objects you can use the ``partial`` +DQL keyword: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username} FROM CmsUser u'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +You use the partial syntax when joining as well: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +"NEW" Operator Syntax +^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.4 + +Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries. + +- When using ``SELECT NEW`` you don't need to specify a mapped entity. +- You can specify any PHP class, it's only require that the constructor of this class matches the ``NEW`` statement. +- This approach involves determining exactly which columns you really need, + and instantiating data-transfer object that containing a constructor with those arguments. + +If you want to select data-transfer objects you should create a class: + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c'); + $users = $query->getResult(); // array of CustomerDTO + +Using INDEX BY +~~~~~~~~~~~~~~ + +The INDEX BY construct is nothing that directly translates into SQL +but that affects object and array hydration. After each FROM and +JOIN clause you specify by which field this class should be indexed +in the result. By default a result is incremented by numerical keys +starting with 0. However with INDEX BY you can specify any other +column to be the key of your result, it really only makes sense +with primary or unique fields though: + +.. code-block:: sql + + SELECT u.id, u.status, upper(u.name) nameUpper FROM User u INDEX BY u.id + JOIN u.phonenumbers p INDEX BY p.phonenumber + +Returns an array of the following kind, indexed by both user-id +then phonenumber-id: + +.. code-block:: php + + array + 0 => + array + 1 => + object(stdClass)[299] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 1 + .. + 'nameUpper' => string 'ROMANB' (length=6) + 1 => + array + 2 => + object(stdClass)[298] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 2 + ... + 'nameUpper' => string 'JWAGE' (length=5) + +UPDATE queries +-------------- + +DQL not only allows to select your Entities using field names, you +can also execute bulk updates on a set of entities using an +DQL-UPDATE query. The Syntax of an UPDATE query works as expected, +as the following example shows: + +.. code-block:: sql + + UPDATE MyProject\Model\User u SET u.password = 'new' WHERE u.id IN (1, 2, 3) + +References to related entities are only possible in the WHERE +clause and using sub-selects. + +.. warning:: + + DQL UPDATE statements are ported directly into a + Database UPDATE statement and therefore bypass any locking scheme, events + and do not increment the version column. Entities that are already + loaded into the persistence context will *NOT* be synced with the + updated database state. It is recommended to call + ``EntityManager#clear()`` and retrieve new instances of any + affected entity. + + +DELETE queries +-------------- + +DELETE queries can also be specified using DQL and their syntax is +as simple as the UPDATE syntax: + +.. code-block:: sql + + DELETE MyProject\Model\User u WHERE u.id = 4 + +The same restrictions apply for the reference of related entities. + +.. warning:: + + DQL DELETE statements are ported directly into a + Database DELETE statement and therefore bypass any events and checks for the + version column if they are not explicitly added to the WHERE clause + of the query. Additionally Deletes of specifies entities are *NOT* + cascaded to related entities even if specified in the metadata. + + +Functions, Operators, Aggregates +-------------------------------- + +DQL Functions +~~~~~~~~~~~~~ + +The following functions are supported in SELECT, WHERE and HAVING +clauses: + + +- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side +- ABS(arithmetic\_expression) +- CONCAT(str1, str2) +- CURRENT\_DATE() - Return the current date +- CURRENT\_TIME() - Returns the current time +- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date + and time. +- LENGTH(str) - Returns the length of the given string +- LOCATE(needle, haystack [, offset]) - Locate the first + occurrence of the substring in the string. +- LOWER(str) - returns the string lowercased. +- MOD(a, b) - Return a MOD b. +- SIZE(collection) - Return the number of elements in the + specified collection +- SQRT(q) - Return the square-root of q. +- SUBSTRING(str, start [, length]) - Return substring of given + string. +- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim + the string by the given trim char, defaults to whitespaces. +- UPPER(str) - Return the upper-case of the given string. +- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are DAY, MONTH) +- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are DAY, MONTH) +- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2. + +Arithmetic operators +~~~~~~~~~~~~~~~~~~~~ + +You can do math in DQL using numeric values, for example: + +.. code-block:: sql + + SELECT person.salary * 1.5 FROM CompanyPerson person WHERE person.salary < 100000 + +Aggregate Functions +~~~~~~~~~~~~~~~~~~~ + +The following aggregate functions are allowed in SELECT and GROUP +BY clauses: AVG, COUNT, MIN, MAX, SUM + +Other Expressions +~~~~~~~~~~~~~~~~~ + +DQL offers a wide-range of additional expressions that are known +from SQL, here is a list of all the supported constructs: + + +- ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a + sub-select this works like the equivalent constructs in SQL. +- ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to + match ranges of arithmetic values. +- ``IN (x1, x2, ...)`` and ``NOT IN (x1, x2, ..)`` can be used to + match a set of given values. +- ``LIKE ..`` and ``NOT LIKE ..`` match parts of a string or text + using % as a wildcard. +- ``IS NULL`` and ``IS NOT NULL`` to check for null values +- ``EXISTS`` and ``NOT EXISTS`` in combination with a sub-select + +Adding your own functions to the DQL language +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default DQL comes with functions that are part of a large basis +of underlying databases. However you will most likely choose a +database platform at the beginning of your project and most likely +never change it. For this cases you can easily extend the DQL +parser with own specialized platform functions. + +You can register custom DQL functions in your ORM Configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The functions have to return either a string, numeric or datetime +value depending on the registered function type. As an example we +will add a MySQL specific FLOOR() functionality. All the given +classes have to implement the base class : + +.. code-block:: php + + walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + } + +We will register the function by calling and can then use it: + +.. code-block:: php + + getConfiguration(); + $config->registerNumericFunction('FLOOR', 'MyProject\Query\MysqlFloor'); + + $dql = "SELECT FLOOR(person.salary * 1.75) FROM CompanyPerson person"; + +Querying Inherited Classes +-------------------------- + +This section demonstrates how you can query inherited classes and +what type of results to expect. + +Single Table +~~~~~~~~~~~~ + +`Single Table Inheritance `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +First we need to setup an example set of entities to use. In this +scenario it is a generic Person and Employee example: + +.. code-block:: php + + setName('test'); + $employee->setDepartment('testing'); + $em->persist($employee); + $em->flush(); + +Now lets run a simple query to retrieve the ``Employee`` we just +created: + +.. code-block:: sql + + SELECT e FROM Entities\Employee e WHERE e.name = 'test' + +If we check the generated SQL you will notice it has some special +conditions added to ensure that we will only get back ``Employee`` +entities: + +.. code-block:: sql + + SELECT p0_.id AS id0, p0_.name AS name1, p0_.department AS department2, + p0_.discr AS discr3 FROM Person p0_ + WHERE (p0_.name = ?) AND p0_.discr IN ('employee') + +Class Table Inheritance +~~~~~~~~~~~~~~~~~~~~~~~ + +`Class Table Inheritance `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +The example for class table inheritance is the same as single +table, you just need to change the inheritance type from +``SINGLE_TABLE`` to ``JOINED``: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + + // example2: using setDql + $q = $em->createQuery(); + $q->setDql('select u from MyProject\Model\User u'); + +Query Result Formats +~~~~~~~~~~~~~~~~~~~~ + +The format in which the result of a DQL SELECT query is returned +can be influenced by a so-called ``hydration mode``. A hydration +mode specifies a particular way in which a SQL result set is +transformed. Each hydration mode has its own dedicated method on +the Query class. Here they are: + + +- ``Query#getResult()``: Retrieves a collection of objects. The + result is either a plain collection of objects (pure) or an array + where the objects are nested in the result rows (mixed). +- ``Query#getSingleResult()``: Retrieves a single object. If the + result contains more than one or no object, an exception is thrown. The + pure/mixed distinction does not apply. +- ``Query#getOneOrNullResult()``: Retrieve a single object. If no + object is found null will be returned. +- ``Query#getArrayResult()``: Retrieves an array graph (a nested + array) that is largely interchangeable with the object graph + generated by ``Query#getResult()`` for read-only purposes. + + .. note:: + + An array graph can differ from the corresponding object + graph in certain scenarios due to the difference of the identity + semantics between arrays and objects. + + + +- ``Query#getScalarResult()``: Retrieves a flat/rectangular result + set of scalar values that can contain duplicate data. The + pure/mixed distinction does not apply. +- ``Query#getSingleScalarResult()``: Retrieves a single scalar + value from the result returned by the dbms. If the result contains + more than a single scalar value, an exception is thrown. The + pure/mixed distinction does not apply. + +Instead of using these methods, you can alternatively use the +general-purpose method +``Query#execute(array $params = array(), $hydrationMode = Query::HYDRATE_OBJECT)``. +Using this method you can directly supply the hydration mode as the +second parameter via one of the Query constants. In fact, the +methods mentioned earlier are just convenient shortcuts for the +execute method. For example, the method ``Query#getResult()`` +internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as +the hydration mode. + +The use of the methods mentioned earlier is generally preferred as +it leads to more concise code. + +Pure and Mixed Results +~~~~~~~~~~~~~~~~~~~~~~ + +The nature of a result returned by a DQL SELECT query retrieved +through ``Query#getResult()`` or ``Query#getArrayResult()`` can be +of 2 forms: **pure** and **mixed**. In the previous simple +examples, you already saw a "pure" query result, with only objects. +By default, the result type is **pure** but +**as soon as scalar values, such as aggregate values or other scalar values that do not belong to an entity, appear in the SELECT part of the DQL query, the result becomes mixed**. +A mixed result has a different structure than a pure result in +order to accommodate for the scalar values. + +A pure result usually looks like this: + +.. code-block:: php + + $dql = "SELECT u FROM User u"; + + array + [0] => Object + [1] => Object + [2] => Object + ... + +A mixed result on the other hand has the following general +structure: + +.. code-block:: php + + $dql = "SELECT u, 'some scalar string', count(u.groups) AS num FROM User u JOIN u.groups g GROUP BY u.id"; + + array + [0] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + [1] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + +To better understand mixed results, consider the following DQL +query: + +.. code-block:: sql + + SELECT u, UPPER(u.name) nameUpper FROM MyProject\Model\User u + +This query makes use of the ``UPPER`` DQL function that returns a +scalar value and because there is now a scalar value in the SELECT +clause, we get a mixed result. + +Conventions for mixed results are as follows: + + +- The object fetched in the FROM clause is always positioned with the key '0'. +- Every scalar without a name is numbered in the order given in the query, starting with 1. +- Every aliased scalar is given with its alias-name as the key. The case of the name is kept. +- If several objects are fetched from the FROM clause they alternate every row. + + +Here is how the result could look like: + +.. code-block:: php + + array + array + [0] => User (Object) + ['nameUpper'] => "ROMAN" + array + [0] => User (Object) + ['nameUpper'] => "JONATHAN" + ... + +And here is how you would access it in PHP code: + +.. code-block:: php + + getName(); + echo "Name UPPER: " . $row['nameUpper']; + } + +Fetching Multiple FROM Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you fetch multiple entities that are listed in the FROM clause then the hydration +will return the rows iterating the different top-level entities. + +.. code-block:: php + + $dql = "SELECT u, g FROM User u, Group g"; + + array + [0] => Object (User) + [1] => Object (Group) + [2] => Object (User) + [3] => Object (Group) + + +Hydration Modes +~~~~~~~~~~~~~~~ + +Each of the Hydration Modes makes assumptions about how the result +is returned to user land. You should know about all the details to +make best use of the different result formats: + +The constants for the different hydration modes are: + + +- Query::HYDRATE\_OBJECT +- Query::HYDRATE\_ARRAY +- Query::HYDRATE\_SCALAR +- Query::HYDRATE\_SINGLE\_SCALAR + +Object Hydration +^^^^^^^^^^^^^^^^ + +Object hydration hydrates the result set into the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_OBJECT); + +Array Hydration +^^^^^^^^^^^^^^^ + +You can run the same query with array hydration and the result set +is hydrated into an array that represents the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_ARRAY); + +You can use the ``getArrayResult()`` shortcut as well: + +.. code-block:: php + + getArrayResult(); + +Scalar Hydration +^^^^^^^^^^^^^^^^ + +If you want to return a flat rectangular result set instead of an +object graph you can use scalar hydration: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_SCALAR); + echo $users[0]['u_id']; + +The following assumptions are made about selected fields using +Scalar Hydration: + + +1. Fields from classes are prefixed by the DQL alias in the result. + A query of the kind 'SELECT u.name ..' returns a key 'u\_name' in + the result rows. + +Single Scalar Hydration +^^^^^^^^^^^^^^^^^^^^^^^ + +If you a query which returns just a single scalar value you can use +single scalar hydration: + +.. code-block:: php + + createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id'); + $query->setParameter(1, 'jwage'); + $numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR); + +You can use the ``getSingleScalarResult()`` shortcut as well: + +.. code-block:: php + + getSingleScalarResult(); + +Custom Hydration Modes +^^^^^^^^^^^^^^^^^^^^^^ + +You can easily add your own custom hydration modes by first +creating a class which extends ``AbstractHydrator``: + +.. code-block:: php + + _stmt->fetchAll(PDO::FETCH_ASSOC); + } + } + +Next you just need to add the class to the ORM configuration: + +.. code-block:: php + + getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator'); + +Now the hydrator is ready to be used in your queries: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $results = $query->getResult('CustomHydrator'); + +Iterating Large Result Sets +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are situations when a query you want to execute returns a +very large result-set that needs to be processed. All the +previously described hydration modes completely load a result-set +into memory which might not be feasible with large result sets. See +the `Batch Processing `_ section on details how +to iterate large result sets. + +Functions +~~~~~~~~~ + +The following methods exist on the ``AbstractQuery`` which both +``Query`` and ``NativeQuery`` extend from. + +Parameters +^^^^^^^^^^ + +Prepared Statements that use numerical or named wildcards require +additional parameters to be executable against the database. To +pass parameters to the query the following methods can be used: + + +- ``AbstractQuery::setParameter($param, $value)`` - Set the + numerical or named wildcard to the given value. +- ``AbstractQuery::setParameters(array $params)`` - Set an array + of parameter key-value pairs. +- ``AbstractQuery::getParameter($param)`` +- ``AbstractQuery::getParameters()`` + +Both named and positional parameters are passed to these methods without their ? or : prefix. + +Cache related API +^^^^^^^^^^^^^^^^^ + +You can cache query results based either on all variables that +define the result (SQL, Hydration Mode, Parameters and Hints) or on +user-defined cache keys. However by default query results are not +cached at all. You have to enable the result cache on a per query +basis. The following example shows a complete workflow using the +Result Cache API: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.id = ?1'); + $query->setParameter(1, 12); + + $query->setResultCacheDriver(new ApcCache()); + + $query->useResultCache(true) + ->setResultCacheLifeTime($seconds = 3600); + + $result = $query->getResult(); // cache miss + + $query->expireResultCache(true); + $result = $query->getResult(); // forced expire, cache miss + + $query->setResultCacheId('my_query_result'); + $result = $query->getResult(); // saved in given result cache id. + + // or call useResultCache() with all parameters: + $query->useResultCache(true, $seconds = 3600, 'my_query_result'); + $result = $query->getResult(); // cache hit! + + // Introspection + $queryCacheProfile = $query->getQueryCacheProfile(); + $cacheDriver = $query->getResultCacheDriver(); + $lifetime = $query->getLifetime(); + $key = $query->getCacheKey(); + +.. note:: + + You can set the Result Cache Driver globally on the + ``Doctrine\ORM\Configuration`` instance so that it is passed to + every ``Query`` and ``NativeQuery`` instance. + + +Query Hints +^^^^^^^^^^^ + +You can pass hints to the query parser and hydrators by using the +``AbstractQuery::setHint($name, $value)`` method. Currently there +exist mostly internal query hints that are not be consumed in +userland. However the following few hints are to be used in +userland: + + +- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects + although not all their columns are fetched. This query hint can be + used to handle memory consumption problems with large result-sets + that contain char or binary data. Doctrine has no way of implicitly + reloading this data. Partially loaded objects have to be passed to + ``EntityManager::refresh()`` if they are to be reloaded fully from + the database. +- Query::HINT\_REFRESH - This query is used internally by + ``EntityManager::refresh()`` and can be used in userland as well. + If you specify this hint and a query returns the data for an entity + that is already managed by the UnitOfWork, the fields of the + existing entity will be refreshed. In normal operation a result-set + that loads data of an already existing entity is discarded in favor + of the already existing entity. +- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional + ``Doctrine\ORM\Query\TreeWalker`` instances that are attached to + the DQL query parsing process. + +Query Cache (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Parsing a DQL query and converting it into a SQL query against the +underlying database platform obviously has some overhead in +contrast to directly executing Native SQL queries. That is why +there is a dedicated Query Cache for caching the DQL parser +results. In combination with the use of wildcards you can reduce +the number of parsed queries in production to zero. + +The Query Cache Driver is passed from the +``Doctrine\ORM\Configuration`` instance to each +``Doctrine\ORM\Query`` instance by default and is also enabled by +default. This also means you don't regularly need to fiddle with +the parameters of the Query Cache, however if you do there are +several methods to interact with it: + + +- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache + instance +- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime + of the query caching. +- ``Query::expireQueryCache($bool)`` - Enforce the expiring of the + query cache if set to true. +- ``Query::getExpireQueryCache()`` +- ``Query::getQueryCacheDriver()`` +- ``Query::getQueryCacheLifeTime()`` + +First and Max Result Items (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can limit the number of results returned from a DQL query as +well as specify the starting offset, Doctrine then uses a strategy +of manipulating the select query to return only the requested +number of results: + + +- ``Query::setMaxResults($maxResults)`` +- ``Query::setFirstResult($offset)`` + +.. note:: + + If your query contains a fetch-joined collection + specifying the result limit methods are not working as you would + expect. Set Max Results restricts the number of database result + rows, however in the case of fetch-joined collections one root + entity might appear in many rows, effectively hydrating less than + the specified number of results. + +.. _dql-temporarily-change-fetch-mode: + +Temporarily change fetch mode in DQL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While normally all your associations are marked as lazy or extra lazy you will have cases where you are using DQL and don't want to +fetch join a second, third or fourth level of entities into your result, because of the increased cost of the SQL JOIN. You +can mark a many-to-one or one-to-one association as fetched temporarily to batch fetch these entities using a WHERE .. IN query. + +.. code-block:: php + + createQuery("SELECT u FROM MyProject\User u"); + $query->setFetchMode("MyProject\User", "address", "EAGER"); + $query->execute(); + +Given that there are 10 users and corresponding addresses in the database the executed queries will look something like: + +.. code-block:: sql + + SELECT * FROM users; + SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + +EBNF +---- + +The following context-free grammar, written in an EBNF variant, +describes the Doctrine Query Language. You can consult this grammar +whenever you are unsure about what is possible with DQL or what the +correct syntax for a particular query should be. + +Document syntax: +~~~~~~~~~~~~~~~~ + + +- non-terminals begin with an upper case character +- terminals begin with a lower case character +- parentheses (...) are used for grouping +- square brackets [...] are used for defining an optional part, + e.g. zero or one time +- curly brackets {...} are used for repetition, e.g. zero or more + times +- double quotation marks "..." define a terminal string a vertical + bar \| represents an alternative + +Terminals +~~~~~~~~~ + + +- identifier (name, email, ...) +- string ('foo', 'bar''s house', '%ninja%', ...) +- char ('/', '\\', ' ', ...) +- integer (-1, 0, 1, 34, ...) +- float (-0.23, 0.007, 1.245342E+8, ...) +- boolean (false, true) + +Query Language +~~~~~~~~~~~~~~ + +.. code-block:: php + + QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + +Statements +~~~~~~~~~~ + +.. code-block:: php + + SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + UpdateStatement ::= UpdateClause [WhereClause] + DeleteStatement ::= DeleteClause [WhereClause] + +Identifiers +~~~~~~~~~~~ + +.. code-block:: php + + /* Alias Identification usage (the "u" of "u.name") */ + IdentificationVariable ::= identifier + + /* Alias Identification declaration (the "u" of "FROM User u") */ + AliasIdentificationVariable :: = identifier + + /* identifier that must be a class name (the "User" of "FROM User u") */ + AbstractSchemaName ::= identifier + + /* identifier that must be a field (the "name" of "u.name") */ + /* This is responsible to know if the field exists in Object, no matter if it's a relation or a simple field */ + FieldIdentificationVariable ::= identifier + + /* identifier that must be a collection-valued association field (to-many) (the "Phonenumbers" of "u.Phonenumbers") */ + CollectionValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be a single-valued association field (to-one) (the "Group" of "u.Group") */ + SingleValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be an embedded class state field (for the future) */ + EmbeddedClassStateField ::= FieldIdentificationVariable + + /* identifier that must be a simple state field (name, email, ...) (the "name" of "u.name") */ + /* The difference between this and FieldIdentificationVariable is only semantical, because it points to a single field (not mapping to a relation) */ + SimpleStateField ::= FieldIdentificationVariable + + /* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */ + AliasResultVariable = identifier + + /* ResultVariable identifier usage of mapped field aliases (the "total" of "COUNT(*) AS total") */ + ResultVariable = identifier + +Path Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /* "u.Group" or "u.Phonenumbers" declarations */ + JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + + /* "u.Group" or "u.Phonenumbers" usages */ + AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group" */ + SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group.name" */ + StateFieldPathExpression ::= IdentificationVariable "." StateField + + /* "u.Group" */ + SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + + /* "u.Group.Permissions" */ + CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + + /* "name" */ + StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + +Clauses +~~~~~~~ + +.. code-block:: php + + SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}* + SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + WhereClause ::= "WHERE" ConditionalExpression + HavingClause ::= "HAVING" ConditionalExpression + GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + +Items +~~~~~ + +.. code-block:: php + + UpdateItem ::= SingleValuedPathExpression "=" NewValue + OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable) ["ASC" | "DESC"] + GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + NewValue ::= SimpleArithmeticExpression | "NULL" + +From, Join and Index by +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + JoinVariableDeclaration ::= Join [IndexBy] + RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression] + IndexBy ::= "INDEX" "BY" StateFieldPathExpression + +Select Expressions +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression) [["AS"] ["HIDDEN"] AliasResultVariable] + SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] + PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + +Conditional Expressions +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + ConditionalFactor ::= ["NOT"] ConditionalPrimary + ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression | + InExpression | NullComparisonExpression | ExistsExpression | + EmptyCollectionComparisonExpression | CollectionMemberExpression | + InstanceOfExpression + + +Collection Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + +Literal Values +~~~~~~~~~~~~~~ + +.. code-block:: php + + Literal ::= string | char | integer | float | boolean + InParameter ::= Literal | InputParameter + +Input Parameter +~~~~~~~~~~~~~~~ + +.. code-block:: php + + InputParameter ::= PositionalParameter | NamedParameter + PositionalParameter ::= "?" integer + NamedParameter ::= ":" string + +Arithmetic Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" + | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + | InputParameter | CaseExpression + +Scalar and Type Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression + StringExpression ::= StringPrimary | "(" Subselect ")" + StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + BooleanExpression ::= BooleanPrimary | "(" Subselect ")" + BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter + EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + SimpleEntityExpression ::= IdentificationVariable | InputParameter + DatetimeExpression ::= DatetimePrimary | "(" Subselect ")" + DatetimePrimary ::= StateFieldPathExpression | InputParameter | FunctionsReturningDatetime | AggregateExpression + +.. note:: + + Parts of CASE expressions are not yet implemented. + +Aggregate Expressions +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" | + "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")" + +Case Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression + GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + +Other Expressions +~~~~~~~~~~~~~~~~~ + +QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS + +.. code-block:: php + + QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + InstanceOfParameter ::= AbstractSchemaName | InputParameter + LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] + NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + +Functions +~~~~~~~~~ + +.. code-block:: php + + FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDateTime + + FunctionsReturningNumerics ::= + "LENGTH" "(" StringPrimary ")" | + "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + "ABS" "(" SimpleArithmeticExpression ")" | + "SQRT" "(" SimpleArithmeticExpression ")" | + "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "SIZE" "(" CollectionValuedPathExpression ")" | + "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + + FunctionsReturningDateTime ::= + "CURRENT_DATE" | + "CURRENT_TIME" | + "CURRENT_TIMESTAMP" | + "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | + "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + + FunctionsReturningStrings ::= + "CONCAT" "(" StringPrimary "," StringPrimary ")" | + "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + "LOWER" "(" StringPrimary ")" | + "UPPER" "(" StringPrimary ")" | + "IDENTITY" "(" SingleValuedAssociationPathExpression ")" + + diff --git a/vendor/doctrine/orm/docs/en/reference/events.rst b/vendor/doctrine/orm/docs/en/reference/events.rst new file mode 100644 index 00000000..517f2ec5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/events.rst @@ -0,0 +1,955 @@ +Events +====== + +Doctrine 2 features a lightweight event system that is part of the +Common package. Doctrine uses it to dispatch system events, mainly +:ref:`lifecycle events `. +You can also use it for your own custom events. + +The Event System +---------------- + +The event system is controlled by the ``EventManager``. It is the +central point of Doctrine's event listener system. Listeners are +registered on the manager and events are dispatched through the +manager. + +.. code-block:: php + + addEventListener(array(self::preFoo, self::postFoo), $this); + } + + public function preFoo(EventArgs $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventArgs $e) + { + $this->postFooInvoked = true; + } + } + + // Create a new instance + $test = new EventTest($evm); + +Events can be dispatched by using the ``dispatchEvent()`` method. + +.. code-block:: php + + dispatchEvent(EventTest::preFoo); + $evm->dispatchEvent(EventTest::postFoo); + +You can easily remove a listener with the ``removeEventListener()`` +method. + +.. code-block:: php + + removeEventListener(array(self::preFoo, self::postFoo), $this); + +The Doctrine 2 event system also has a simple concept of event +subscribers. We can define a simple ``TestEventSubscriber`` class +which implements the ``\Doctrine\Common\EventSubscriber`` interface +and implements a ``getSubscribedEvents()`` method which returns an +array of events it should be subscribed to. + +.. code-block:: php + + preFooInvoked = true; + } + + public function getSubscribedEvents() + { + return array(TestEvent::preFoo); + } + } + + $eventSubscriber = new TestEventSubscriber(); + $evm->addEventSubscriber($eventSubscriber); + +.. note:: + + The array to return in 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 for that event. + +.. code-block:: php + + dispatchEvent(TestEvent::preFoo); + +Now you can test the ``$eventSubscriber`` instance to see if the +``preFoo()`` method was invoked. + +.. code-block:: php + + preFooInvoked) { + echo 'pre foo invoked!'; + } + +Naming convention +~~~~~~~~~~~~~~~~~ + +Events being used with the Doctrine 2 EventManager are best named +with camelcase and the value of the corresponding constant should +be the name of the constant itself, even with spelling. This has +several reasons: + + +- It is easy to read. +- Simplicity. +- Each method within an EventSubscriber is named after the + corresponding constant. If constant name and constant value differ, + you MUST use the new value and thus, your code might be subject to + codechanges when the value changes. This contradicts the intention + of a constant. + +An example for a correct notation can be found in the example +``EventTest`` above. + +.. _reference-events-lifecycle-events: + +Lifecycle Events +---------------- + +The EntityManager and UnitOfWork trigger a bunch of events during +the life-time of their registered entities. + + +- preRemove - The preRemove event occurs for a given entity before + the respective EntityManager remove operation for that entity is + executed. It is not called for a DQL DELETE statement. +- postRemove - The postRemove event occurs for an entity after the + entity has been deleted. It will be invoked after the database + delete operations. It is not called for a DQL DELETE statement. +- prePersist - The prePersist event occurs for a given entity + before the respective EntityManager persist operation for that + entity is executed. It should be noted that this event is only triggered on + *initial* persist of an entity +- postPersist - The postPersist event occurs for an entity after + the entity has been made persistent. It will be invoked after the + database insert operations. Generated primary key values are + available in the postPersist event. +- preUpdate - The preUpdate event occurs before the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postUpdate - The postUpdate event occurs after the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postLoad - The postLoad event occurs for an entity after the + entity has been loaded into the current EntityManager from the + database or after the refresh operation has been applied to it. +- loadClassMetadata - The loadClassMetadata event occurs after the + mapping metadata for a class has been loaded from a mapping source + (annotations/xml/yaml). +- preFlush - The preFlush event occurs at the very beginning of a flush + operation. This event is not a lifecycle callback. +- onFlush - The onFlush event occurs after the change-sets of all + managed entities are computed. This event is not a lifecycle + callback. +- postFlush - The postFlush event occurs at the end of a flush operation. This + event is not a lifecycle callback. +- onClear - The onClear event occurs when the EntityManager#clear() operation is + invoked, after all references to entities have been removed from the unit of + work. + +.. warning:: + + Note that the postLoad event occurs for an entity + before any associations have been initialized. Therefore it is not + safe to access associations in a postLoad callback or event + handler. + + +You can access the Event constants from the ``Events`` class in the +ORM package. + +.. code-block:: php + + createdAt = date('Y-m-d H:i:s'); + } + + /** @PrePersist */ + public function doOtherStuffOnPrePersist() + { + $this->value = 'changed from prePersist callback!'; + } + + /** @PostPersist */ + public function doStuffOnPostPersist() + { + $this->value = 'changed from postPersist callback!'; + } + + /** @PostLoad */ + public function doStuffOnPostLoad() + { + $this->value = 'changed from postLoad callback!'; + } + + /** @PreUpdate */ + public function doStuffOnPreUpdate() + { + $this->value = 'changed from preUpdate callback!'; + } + } + +Note that when using annotations you have to apply the +@HasLifecycleCallbacks marker annotation on the entity class. + +If you want to register lifecycle callbacks from YAML or XML you +can do it with the following. + +.. code-block:: yaml + + User: + type: entity + fields: + # ... + name: + type: string(50) + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +XML would look something like this: + +.. code-block:: xml + + + + + + + + + + + + + + + + +You just need to make sure a public ``doStuffOnPrePersist()`` and +``doStuffOnPostPersist()`` method is defined on your ``User`` +model. + +.. code-block:: php + + hasChangedField('username')) { + // Do something when the username is changed. + } + } + } + +Listening and subscribing to Lifecycle Events +--------------------------------------------- + +Lifecycle event listeners are much more powerful than the simple +lifecycle callbacks that are defined on the entity classes. They +allow to implement re-usable behaviors between different entity +classes, yet require much more detailed knowledge about the inner +workings of the EntityManager and UnitOfWork. Please read the +*Implementing Event Listeners* section carefully if you are trying +to write your own listener. + +For event subscribers, there are no surprises. They declare the +lifecycle events in their ``getSubscribedEvents`` method and provide +public methods that expect the relevant arguments. + +A lifecycle event listener looks like the following: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + } + +A lifecycle event subscriber may looks like this: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + +.. note:: + + Lifecycle events are triggered for all entities. It is the responsibility + of the listeners and subscribers to check if the entity is of a type + it wants to handle. + +To register an event listener or subscriber, you have to hook it into the +EventManager that is passed to the EntityManager factory: + +.. code-block:: php + + addEventListener(array(Events::preUpdate), new MyEventListener()); + $eventManager->addEventSubscriber(new MyEventSubscriber()); + + $entityManager = EntityManager::create($dbOpts, $config, $eventManager); + +You can also retrieve the event manager instance after the +EntityManager was created: + +.. code-block:: php + + getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener()); + $entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber()); + +.. _reference-events-implementing-listeners: + +Implementing Event Listeners +---------------------------- + +This section explains what is and what is not allowed during +specific lifecycle events of the UnitOfWork. Although you get +passed the EntityManager in all of these events, you have to follow +these restrictions very carefully since operations in the wrong +event may produce lots of different errors, such as inconsistent +data and lost updates/persists/removes. + +For the described events that are also lifecycle callback events +the restrictions apply as well, with the additional restriction +that you do not have access to the EntityManager or UnitOfWork APIs +inside these events. + +prePersist +~~~~~~~~~~ + +There are two ways for the ``prePersist`` event to be triggered. +One is obviously when you call ``EntityManager#persist()``. The +event is also called for all cascaded associations. + +There is another way for ``prePersist`` to be called, inside the +``flush()`` method when changes to associations are computed and +this association is marked as cascade persist. Any new entity found +during this operation is also persisted and ``prePersist`` called +on it. This is called "persistence by reachability". + +In both cases you get passed a ``LifecycleEventArgs`` instance +which has access to the entity and the entity manager. + +The following restrictions apply to ``prePersist``: + + +- If you are using a PrePersist Identity Generator such as + sequences the ID value will *NOT* be available within any + PrePersist events. +- Doctrine will not recognize changes made to relations in a pre + persist event called by "reachability" through a cascade persist + unless you use the internal ``UnitOfWork`` API. We do not recommend + such operations in the persistence by reachability context, so do + this at your own risk and possibly supported by unit-tests. + +preRemove +~~~~~~~~~ + +The ``preRemove`` event is called on every entity when its passed +to the ``EntityManager#remove()`` method. It is cascaded for all +associations that are marked as cascade delete. + +There are no restrictions to what methods can be called inside the +``preRemove`` event, except when the remove method itself was +called during a flush operation. + +preFlush +~~~~~~~~ + +``preFlush`` is called at ``EntityManager#flush()`` before +anything else. ``EntityManager#flush()`` can be called safely +inside its listeners. + +.. code-block:: php + + getEntityManager(); + $uow = $em->getUnitOfWork(); + + foreach ($uow->getScheduledEntityInsertions() AS $entity) { + + } + + foreach ($uow->getScheduledEntityUpdates() AS $entity) { + + } + + foreach ($uow->getScheduledEntityDeletions() AS $entity) { + + } + + foreach ($uow->getScheduledCollectionDeletions() AS $col) { + + } + + foreach ($uow->getScheduledCollectionUpdates() AS $col) { + + } + } + } + +The following restrictions apply to the onFlush event: + + +- If you create and persist a new entity in "onFlush", then + calling ``EntityManager#persist()`` is not enough. + You have to execute an additional call to + ``$unitOfWork->computeChangeSet($classMetadata, $entity)``. +- Changing primitive fields or associations requires you to + explicitly trigger a re-computation of the changeset of the + affected entity. This can be done by either calling + ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``. + +postFlush +~~~~~~~~~ + +``postFlush`` is called at the end of ``EntityManager#flush()``. +``EntityManager#flush()`` can **NOT** be called safely inside its listeners. + +.. code-block:: php + + getEntity() instanceof User) { + if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') { + $eventArgs->setNewValue('name', 'Bob'); + } + } + } + } + +You could also use this listener to implement validation of all the +fields that have changed. This is more efficient than using a +lifecycle callback when there are expensive validations to call: + +.. code-block:: php + + getEntity() instanceof Account) { + if ($eventArgs->hasChangedField('creditCard')) { + $this->validateCreditCard($eventArgs->getNewValue('creditCard')); + } + } + } + + private function validateCreditCard($no) + { + // throw an exception to interrupt flush event. Transaction will be rolled back. + } + } + +Restrictions for this event: + + +- Changes to associations of the passed entities are not + recognized by the flush operation anymore. +- Changes to fields of the passed entities are not recognized by + the flush operation anymore, use the computed change-set passed to + the event to modify primitive field values. +- Any calls to ``EntityManager#persist()`` or + ``EntityManager#remove()``, even in combination with the UnitOfWork + API are strongly discouraged and don't work as expected outside the + flush operation. + +postUpdate, postRemove, postPersist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The three post events are called inside ``EntityManager#flush()``. +Changes in here are not relevant to the persistence in the +database, but you can use these events to alter non-persistable items, +like non-mapped fields, logging or even associated classes that are +directly mapped by Doctrine. + +postLoad +~~~~~~~~ + +This event is called after an entity is constructed by the +EntityManager. + +Entity listeners +---------------- + +.. versionadded:: 2.4 + +An entity listeners is a lifecycle listener classes used for an entity. + +- The entity listeners mapping may be applied to an entity class or mapped superclass. +- An entity listener is defined by mapping the entity class with the corresponding mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + # .... + +.. _reference-entity-listeners: + +Entity listeners class +~~~~~~~~~~~~~~~~~~~~~~ + +An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor. + +- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity +- An entity listener method receives two arguments, the entity instance and the lifecycle event. +- A callback method could be defined by naming convention or specifying a method mapping. +- When the listener mapping is not given the parser will lookup for methods that match with the naming convention. +- When the listener mapping is given the parser won't lookup for any naming convention. + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + preFlush: [preFlushHandler] + postLoad: [postLoadHandler] + + postPersist: [postPersistHandler] + prePersist: [prePersistHandler] + + postUpdate: [postUpdateHandler] + preUpdate: [preUpdateHandler] + + postRemove: [postRemoveHandler] + preRemove: [preRemoveHandler] + # .... + + + +Entity listeners resolver +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Doctrine invoke the listener resolver to get the listener instance. + +- An resolver allows you register a specific ``Entity Listener`` instance. +- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver`` + +Specifying an entity listener instance : + +.. code-block:: php + + service = $service; + } + + public function preUpdate(User $user, PreUpdateEventArgs $event) + { + $this->service->doSomething($user); + } + } + + // register a entity listener. + $listener = $container->get('user_listener'); + $em->getConfiguration()->getEntityListenerResolver()->register($listener); + +Implementing your own resolver : + +.. code-block:: php + + container = $container; + } + + public function resolve($className) + { + // resolve the service id by the given class name; + $id = 'user_listener'; + + return $this->container->get($id); + } + } + + // configure the listener resolver. + $em->getConfiguration()->setEntityListenerResolver($container->get('my_resolver')); + +Load ClassMetadata Event +------------------------ + +When the mapping information for an entity is read, it is populated +in to a ``ClassMetadataInfo`` instance. You can hook in to this +process and manipulate the instance. + +.. code-block:: php + + getMetadataFactory(); + $evm = $em->getEventManager(); + $evm->addEventListener(Events::loadClassMetadata, $test); + + class EventTest + { + public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + $fieldMapping = array( + 'fieldName' => 'about', + 'type' => 'string', + 'length' => 255 + ); + $classMetadata->mapField($fieldMapping); + } + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/faq.rst b/vendor/doctrine/orm/docs/en/reference/faq.rst new file mode 100644 index 00000000..28e3a42c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/faq.rst @@ -0,0 +1,224 @@ +Frequently Asked Questions +========================== + +.. note:: + + This FAQ is a work in progress. We will add lots of questions and not answer them right away just to remember + what is often asked. If you stumble across an unanswered question please write a mail to the mailing-list or + join the #doctrine channel on Freenode IRC. + +Database Schema +--------------- + +How do I set the charset and collation for MySQL tables? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can't set these values inside the annotations, yml or xml mapping files. To make a database +work with the default charset and collation you should configure MySQL to use it as default charset, +or create the database with charset and collation details. This way they get inherited to all newly +created database tables and columns. + +Entity Classes +-------------- + +I access a variable and its null, what is wrong? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If this variable is a public variable then you are violating one of the criteria for entities. +All properties have to be protected or private for the proxy object pattern to work. + +How can I add default values to a column? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL. +This is not necessary however, you can just use your class properties as default values. These are then used +upon insert: + +.. code-block:: php + + class User + { + const STATUS_DISABLED = 0; + const STATUS_ENABLED = 1; + + private $algorithm = "sha1"; + private $status = self:STATUS_DISABLED; + } + +. + +Mapping +------- + +Why do I get exceptions about unique constraint failures during ``$em->flush()``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not check if you are re-adding entities with a primary key that already exists +or adding entities to a collection twice. You have to check for both conditions yourself +in the code before calling ``$em->flush()`` if you know that unique constraint failures +can occur. + +In `Symfony2 `_ for example there is a Unique Entity Validator +to achieve this task. + +For collections you can check with ``$collection->contains($entity)`` if an entity is already +part of this collection. For a FETCH=LAZY collection this will initialize the collection, +however for FETCH=EXTRA_LAZY this method will use SQL to determine if this entity is already +part of the collection. + +Associations +------------ + +What is wrong when I get an InvalidArgumentException "A new entity was found through the relationship.."? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This exception is thrown during ``EntityManager#flush()`` when there exists an object in the identity map +that contains a reference to an object that Doctrine does not know about. Say for example you grab +a "User"-entity from the database with a specific id and set a completely new object into one of the associations +of the User object. If you then call ``EntityManager#flush()`` without letting Doctrine know about +this new object using ``EntityManager#persist($newObject)`` you will see this exception. + +You can solve this exception by: + +* Calling ``EntityManager#persist($newObject)`` on the new object +* Using cascade=persist on the association that contains the new object + +How can I filter an association? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities. + +I call clear() on a One-To-Many collection but the entities are not deleted +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is an expected behavior that has to do with the inverse/owning side handling of Doctrine. +By definition a One-To-Many association is on the inverse side, that means changes to it +will not be recognized by Doctrine. + +If you want to perform the equivalent of the clear operation you have to iterate the +collection and set the owning side many-to-one reference to NULL as well to detach all entities +from the collection. This will trigger the appropriate UPDATE statements on the database. + +How can I add columns to a many-to-many table? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-many association is only supporting foreign keys in the table definition +To work with many-to-many tables containing extra columns you have to use the +foreign keys as primary keys feature of Doctrine introduced in version 2.1. + +See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`. + + +How can i paginate fetch-joined collections? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are issuing a DQL statement that fetches a collection as well you cannot easily iterate +over this collection using a LIMIT statement (or vendor equivalent). + +Doctrine does not offer a solution for this out of the box but there are several extensions +that do: + +* `DoctrineExtensions `_ +* `Pagerfanta `_ + +Why does pagination not work correctly with fetch joins? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pagination in Doctrine uses a LIMIT clause (or vendor equivalent) to restrict the results. +However when fetch-joining this is not returning the correct number of results since joining +with a one-to-many or many-to-many association multiplies the number of rows by the number +of associated entities. + +See the previous question for a solution to this task. + +Inheritance +----------- + +Can I use Inheritance with Doctrine 2? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2. + +See the documentation chapter on :doc:`inheritance mapping ` for +the details. + +Why does Doctrine not create proxy objects for my inheritance hierarchy? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you set a many-to-one or one-to-one association target-entity to any parent class of +an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of. +To find this out it has to execute a SQL query to look this information up in the database. + +EntityGenerator +--------------- + +Why does the EntityGenerator not do X? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation +is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator +is supposed to kick-start you, but not towards 100%. + +Why does the EntityGenerator not generate inheritance correctly? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy. +This is why the generation of inherited entities does not fully work. You have to adjust some additional +code to get this one working correctly. + +Performance +----------- + +Why is an extra SQL query executed every time I fetch an entity with a one-to-one relation? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If Doctrine detects that you are fetching an inverse side one-to-one association +it has to execute an additional query to load this object, because it cannot know +if there is no such object (setting null) or if it should set a proxy and which id this proxy has. + +To solve this problem currently a query has to be executed to find out this information. + +Doctrine Query Language +----------------------- + +What is DQL? +~~~~~~~~~~~~ + +DQL stands for Doctrine Query Language, a query language that very much looks like SQL +but has some important benefits when using Doctrine: + +- It uses class names and fields instead of tables and columns, separating concerns between backend and your object model. +- It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them. +- It adds some functionality that is related to object management and transforms them into SQL. + +It also has some drawbacks of course: + +- The syntax is slightly different to SQL so you have to learn and remember the differences. +- To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly. +- For some DQL constructs subselects are used which are known to be slow in MySQL. + +Can I sort by a function (for example ORDER BY RAND()) in DQL? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, it is not supported to sort by function in DQL. If you need this functionality you should either +use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow +starting with 1000 rows. + +A Query fails, how can I debug it? +---------------------------------- + +First, if you are using the QueryBuilder you can use +``$queryBuilder->getDQL()`` to get the DQL string of this query. The +corresponding SQL you can get from the Query instance by calling +``$query->getSQL()``. + +.. code-block:: php + + createQuery($dql); + var_dump($query->getSQL()); + + $qb = $entityManager->createQueryBuilder(); + $qb->select('u')->from('User', 'u'); + var_dump($qb->getDQL()); diff --git a/vendor/doctrine/orm/docs/en/reference/filters.rst b/vendor/doctrine/orm/docs/en/reference/filters.rst new file mode 100644 index 00000000..a5c0ee4c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/filters.rst @@ -0,0 +1,93 @@ +Filters +======= + +.. versionadded:: 2.2 + +Doctrine 2.2 features a filter system that allows the developer to add SQL to +the conditional clauses of queries, regardless the place where the SQL is +generated (e.g. from a DQL query, or by loading associated entities). + +The filter functionality works on SQL level. Whether a SQL query is generated +in a Persister, during lazy loading, in extra lazy collections or from DQL. +Each time the system iterates over all the enabled filters, adding a new SQL +part as a filter returns. + +By adding SQL to the conditional clauses of queries, the filter system filters +out rows belonging to the entities at the level of the SQL result set. This +means that the filtered entities are never hydrated (which can be expensive). + + +Example filter class +-------------------- +Throughout this document the example ``MyLocaleFilter`` class will be used to +illustrate how the filter feature works. A filter class must extend the base +``Doctrine\ORM\Query\Filter\SQLFilter`` class and implement the ``addFilterConstraint`` +method. The method receives the ``ClassMetadata`` of the filtered entity and the +table alias of the SQL table of the entity. + +.. note:: + + In the case of joined or single table inheritance, you always get passed the ClassMetadata of the + inheritance root. This is necessary to avoid edge cases that would break the SQL when applying the filters. + +Parameters for the query should be set on the filter object by +``SQLFilter#setParameter()``. Only parameters set via this function can be used +in filters. The ``SQLFilter#getParameter()`` function takes care of the +proper quoting of parameters. + +.. code-block:: php + + reflClass->implementsInterface('LocaleAware')) { + return ""; + } + + return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); // getParameter applies quoting automatically + } + } + + +Configuration +------------- +Filter classes are added to the configuration as following: + +.. code-block:: php + + addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter"); + + +The ``Configuration#addFilter()`` method takes a name for the filter and the name of the +class responsible for the actual filtering. + + +Disabling/Enabling Filters and Setting Parameters +--------------------------------------------------- +Filters can be disabled and enabled via the ``FilterCollection`` which is +stored in the ``EntityManager``. The ``FilterCollection#enable($name)`` method +will retrieve the filter object. You can set the filter parameters on that +object. + +.. code-block:: php + + getFilters()->enable("locale"); + $filter->setParameter('locale', 'en'); + + // Disable it + $filter = $em->getFilters()->disable("locale"); + +.. warning:: + Disabling and enabling filters has no effect on managed entities. If you + want to refresh or reload an object after having modified a filter or the + FilterCollection, then you should clear the EntityManager and re-fetch your + entities, having the new rules for filtering applied. diff --git a/vendor/doctrine/orm/docs/en/reference/improving-performance.rst b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst new file mode 100644 index 00000000..9bc4c863 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst @@ -0,0 +1,64 @@ +Improving Performance +===================== + +Bytecode Cache +-------------- + +It is highly recommended to make use of a bytecode cache like APC. +A bytecode cache removes the need for parsing PHP code on every +request and can greatly improve performance. + + "If you care about performance and don't use a bytecode + cache then you don't really care about performance. Please get one + and start using it." + + *Stas Malyshev, Core Contributor to PHP and Zend Employee* + + +Metadata and Query caches +------------------------- + +As already mentioned earlier in the chapter about configuring +Doctrine, it is strongly discouraged to use Doctrine without a +Metadata and Query cache (preferably with APC or Memcache as the +cache driver). Operating Doctrine without these caches means +Doctrine will need to load your mapping information on every single +request and has to parse each DQL query on every single request. +This is a waste of resources. + +Alternative Query Result Formats +-------------------------------- + +Make effective use of the available alternative query result +formats like nested array graphs or pure scalar results, especially +in scenarios where data is loaded for read-only purposes. + +Read-Only Entities +------------------ + +Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping +references for details). This means that the entity marked as read only is never considered +for updates, which means when you call flush on the EntityManager these entities are skipped +even if properties changed. Read-Only allows to persist new entities of a kind and remove existing +ones, they are just not considered for updates. + +Extra-Lazy Collections +---------------------- + +If entities hold references to large collections you will get performance and memory problems initializing them. +To solve this issue you can use the EXTRA_LAZY fetch-mode feature for collections. See the :doc:`tutorial <../tutorials/extra-lazy-associations>` +for more information on how this fetch mode works. + +Temporarily change fetch mode in DQL +------------------------------------ + +See :ref:`Doctrine Query Language chapter ` + + +Apply Best Practices +-------------------- + +A lot of the points mentioned in the Best Practices chapter will +also positively affect the performance of Doctrine. + + diff --git a/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst new file mode 100644 index 00000000..1bc92f3f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst @@ -0,0 +1,559 @@ +Inheritance Mapping +=================== + +Mapped Superclasses +------------------- + +A mapped superclass is an abstract or concrete class that provides +persistent entity state and mapping information for its subclasses, +but which is not itself an entity. Typically, the purpose of such a +mapped superclass is to define state and mapping information that +is common to multiple entity classes. + +Mapped superclasses, just as regular, non-mapped classes, can +appear in the middle of an otherwise mapped inheritance hierarchy +(through Single Table Inheritance or Class Table Inheritance). + +.. note:: + + A mapped superclass cannot be an entity, it is not query-able and + persistent relationships defined by a mapped superclass must be + unidirectional (with an owning side only). This means that One-To-Many + associations are not possible on a mapped superclass at all. + Furthermore Many-To-Many associations are only possible if the + mapped superclass is only used in exactly one entity at the moment. + For further support of inheritance, the single or + joined table inheritance features have to be used. + + +Example: + +.. code-block:: php + + `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +Example: + +.. code-block:: php + + `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +Example: + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + # other fields mapping + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist, merge ] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist, merge, detach ] + + # admin mapping + MyProject\Model\Admin: + type: entity + associationOverride: + address: + joinColumn: + adminaddress_id: + name: adminaddress_id + referencedColumnName: id + groups: + joinTable: + name: users_admingroups + joinColumns: + adminuser_id: + referencedColumnName: id + inverseJoinColumns: + admingroup_id: + referencedColumnName: id + + +Things to note: + +- The "association override" specifies the overrides base on the property name. +- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany) +- The association type *CANNOT* be changed. +- The override could redefine the joinTables or joinColumns depending on the association type. + +Attribute Override +~~~~~~~~~~~~~~~~~~~~ +Override the mapping of a field. + +Could be used by an entity that extends a mapped superclass to override a field mapping defined by the mapped superclass. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + id: + id: + type: integer + column: user_id + length: 150 + generator: + strategy: AUTO + fields: + name: + type: string + column: user_name + length: 250 + nullable: true + unique: false + #other fields mapping + + + # guest mapping + MyProject\Model\Guest: + type: entity + attributeOverride: + id: + column: guest_id + type: integer + length: 140 + name: + column: guest_name + type: string + length: 240 + nullable: false + unique: true + +Things to note: + +- The "attribute override" specifies the overrides base on the property name. +- The column type *CANNOT* be changed. if the column type is not equals you got a ``MappingException`` +- The override can redefine all the column except the type. diff --git a/vendor/doctrine/orm/docs/en/reference/installation.rst b/vendor/doctrine/orm/docs/en/reference/installation.rst new file mode 100644 index 00000000..8b732ad6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/installation.rst @@ -0,0 +1,5 @@ +Installation +============ + +The installation chapter has moved to `Installation and Configuration +`_. diff --git a/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst new file mode 100644 index 00000000..e3eb498a --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst @@ -0,0 +1,189 @@ +Limitations and Known Issues +============================ + +We try to make using Doctrine2 a very pleasant experience. +Therefore we think it is very important to be honest about the +current limitations to our users. Much like every other piece of +software Doctrine2 is not perfect and far from feature complete. +This section should give you an overview of current limitations of +Doctrine 2 as well as critical known issues that you should know +about. + +Current Limitations +------------------- + +There is a set of limitations that exist currently which might be +solved in the future. Any of this limitations now stated has at +least one ticket in the Tracker and is discussed for future +releases. + +Join-Columns with non-primary keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to use join columns pointing to non-primary keys. Doctrine will think these are the primary +keys and create lazy-loading proxies with the data, which can lead to unexpected results. Doctrine can for performance +reasons not validate the correctness of this settings at runtime but only through the Validate Schema command. + +Mapping Arrays to a Join Table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Related to the previous limitation with "Foreign Keys as +Identifier" you might be interested in mapping the same table +structure as given above to an array. However this is not yet +possible either. See the following example: + +.. code-block:: sql + + CREATE TABLE product ( + id INTEGER, + name VARCHAR, + PRIMARY KEY(id) + ); + + CREATE TABLE product_attributes ( + product_id INTEGER, + attribute_name VARCHAR, + attribute_value VARCHAR, + PRIMARY KEY (product_id, attribute_name) + ); + +This schema should be mapped to a Product Entity as follows: + +.. code-block:: php + + class Product + { + private $id; + private $name; + private $attributes = array(); + } + +Where the ``attribute_name`` column contains the key and +``attribute_value`` contains the value of each array element in +``$attributes``. + +The feature request for persistence of primitive value arrays +`is described in the DDC-298 ticket `_. + +Value Objects +~~~~~~~~~~~~~ + +There is currently no native support value objects in Doctrine +other than for ``DateTime`` instances or if you serialize the +objects using ``serialize()/deserialize()`` which the DBAL Type +"object" supports. + +The feature request for full value-object support +`is described in the DDC-93 ticket `_. + + +Cascade Merge with Bi-directional Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two bugs now that concern the use of cascade merge in combination with bi-directional associations. +Make sure to study the behavior of cascade merge if you are using it: + +- `DDC-875 `_ Merge can sometimes add the same entity twice into a collection +- `DDC-763 `_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability" + +Custom Persisters +~~~~~~~~~~~~~~~~~ + +A Persister in Doctrine is an object that is responsible for the +hydration and write operations of an entity against the database. +Currently there is no way to overwrite the persister implementation +for a given entity, however there are several use-cases that can +benefit from custom persister implementations: + + +- `Add Upsert Support `_ +- `Evaluate possible ways in which stored-procedures can be used `_ +- The previous Filter Rules Feature Request + +Persist Keys of Collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PHP Arrays are ordered hash-maps and so should be the +``Doctrine\Common\Collections\Collection`` interface. We plan to +evaluate a feature that optionally persists and hydrates the keys +of a Collection instance. + +`Ticket DDC-213 `_ + +Mapping many tables to one entity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to map several equally looking tables onto one +entity. For example if you have a production and an archive table +of a certain business concept then you cannot have both tables map +to the same entity. + +Behaviors +~~~~~~~~~ + +Doctrine 2 will **never** include a behavior system like Doctrine 1 +in the core library. We don't think behaviors add more value than +they cost pain and debugging hell. Please see the many different +blog posts we have written on this topics: + +- `Doctrine2 "Behaviors" in a Nutshell `_ +- `A re-usable Versionable behavior for Doctrine2 `_ +- `Write your own ORM on top of Doctrine2 `_ +- `Doctrine 2 Behavioral Extensions `_ +- `Doctrator _ + +Doctrine 2 has enough hooks and extension points so that **you** can +add whatever you want on top of it. None of this will ever become +core functionality of Doctrine2 however, you will have to rely on +third party extensions for magical behaviors. + +Nested Set +~~~~~~~~~~ + +NestedSet was offered as a behavior in Doctrine 1 and will not be +included in the core of Doctrine 2. However there are already two +extensions out there that offer support for Nested Set with +Doctrine 2: + + +- `Doctrine2 Hierarchical-Structural Behavior `_ +- `Doctrine2 NestedSet `_ + +Known Issues +------------ + +The Known Issues section describes critical/blocker bugs and other +issues that are either complicated to fix, not fixable due to +backwards compatibility issues or where no simple fix exists (yet). +We don't plan to add every bug in the tracker there, just those +issues that can potentially cause nightmares or pain of any sort. + +See the Open Bugs on Jira for more details on `bugs, improvement and feature +requests +`_. + +Identifier Quoting and Legacy Databases +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For compatibility reasons between all the supported vendors and +edge case problems Doctrine 2 does **NOT** do automatic identifier +quoting. This can lead to problems when trying to get +legacy-databases to work with Doctrine 2. + + +- You can quote column-names as described in the + :doc:`Basic-Mapping ` section. +- You cannot quote join column names. +- You cannot use non [a-zA-Z0-9\_]+ characters, they will break + several SQL statements. + +Having problems with these kind of column names? Many databases +support all CRUD operations on views that semantically map to +certain tables. You can create views for all your problematic +tables and column names to avoid the legacy quoting nightmare. + +Microsoft SQL Server and Doctrine "datetime" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine assumes that you use ``DateTime2`` data-types. If your legacy database contains DateTime +datatypes then you have to add your own data-type (see Basic Mapping for an example). diff --git a/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst new file mode 100644 index 00000000..6b9cb31e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst @@ -0,0 +1,194 @@ +Metadata Drivers +================ + +The heart of an object relational mapper is the mapping information +that glues everything together. It instructs the EntityManager how +it should behave when dealing with the different entities. + +Core Metadata Drivers +--------------------- + +Doctrine provides a few different ways for you to specify your +metadata: + + +- **XML files** (XmlDriver) +- **Class DocBlock Annotations** (AnnotationDriver) +- **YAML files** (YamlDriver) +- **PHP Code in files or static functions** (PhpDriver) + +Something important to note about the above drivers is they are all +an intermediate step to the same end result. The mapping +information is populated to ``Doctrine\ORM\Mapping\ClassMetadata`` +instances. So in the end, Doctrine only ever has to work with the +API of the ``ClassMetadata`` class to get mapping information for +an entity. + +.. note:: + + The populated ``ClassMetadata`` instances are also cached + so in a production environment the parsing and populating only ever + happens once. You can configure the metadata cache implementation + using the ``setMetadataCacheImpl()`` method on the + ``Doctrine\ORM\Configuration`` class: + + .. code-block:: php + + getConfiguration()->setMetadataCacheImpl(new ApcCache()); + + +If you want to use one of the included core metadata drivers you +just need to configure it. All the drivers are in the +``Doctrine\ORM\Mapping\Driver`` namespace: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Implementing Metadata Drivers +----------------------------- + +In addition to the included metadata drivers you can very easily +implement your own. All you need to do is define a class which +implements the ``Driver`` interface: + +.. code-block:: php + + _loadMappingFile($file); + + // populate ClassMetadataInfo instance from $data + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + // parse contents of $file and return php data structure + } + } + +.. note:: + + When using the ``AbstractFileDriver`` it requires that you + only have one entity defined per file and the file named after the + class described inside where namespace separators are replaced by + periods. So if you have an entity named ``Entities\User`` and you + wanted to write a mapping file for your driver above you would need + to name the file ``Entities.User.dcm.ext`` for it to be + recognized. + + +Now you can use your ``MyMetadataDriver`` implementation by setting +it with the ``setMetadataDriverImpl()`` method: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +ClassMetadata +------------- + +The last piece you need to know and understand about metadata in +Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to +be familiar with them in order to implement your own drivers but +more importantly to retrieve mapping information for a certain +entity when needed. + +You have all the methods you need to manually specify the mapping +information instead of using some mapping file to populate it from. +The base ``ClassMetadataInfo`` class is responsible for only data +storage and is not meant for runtime use. It does not require that +the class actually exists yet so it is useful for describing some +entity before it exists and using that information to generate for +example the entities themselves. The class ``ClassMetadata`` +extends ``ClassMetadataInfo`` and adds some functionality required +for runtime usage and requires that the PHP class is present and +can be autoloaded. + +You can read more about the API of the ``ClassMetadata`` classes in +the PHP Mapping chapter. + +Getting ClassMetadata Instances +------------------------------- + +If you want to get the ``ClassMetadata`` instance for an entity in +your project to programmatically use some mapping information to +generate some HTML or something similar you can retrieve it through +the ``ClassMetadataFactory``: + +.. code-block:: php + + getMetadataFactory(); + $class = $cmf->getMetadataFor('MyEntityName'); + +Now you can learn about the entity and use the data stored in the +``ClassMetadata`` instance to get all mapped fields for example and +iterate over them: + +.. code-block:: php + + fieldMappings as $fieldMapping) { + echo $fieldMapping['fieldName'] . "\n"; + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst new file mode 100644 index 00000000..e1a29cd3 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst @@ -0,0 +1,150 @@ +Implementing a NamingStrategy +============================== + +.. versionadded:: 2.3 + +Using a naming strategy you can provide rules for automatically generating +database identifiers, columns and tables names +when the table/column name is not given. +This feature helps reduce the verbosity of the mapping document, +eliminating repetitive noise (eg: ``TABLE_``). + + +Configuring a naming strategy +----------------------------- +The default strategy used by Doctrine is quite minimal. + +By default the ``Doctrine\ORM\Mapping\DefaultNamingStrategy`` +uses the simple class name and the attributes names to generate tables and columns + +You can specify a different strategy by calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` : + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Underscore naming strategy +--------------------------- + +``\Doctrine\ORM\Mapping\UnderscoreNamingStrategy`` is a built-in strategy +that might be a useful if you want to use a underlying convention. + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Then SomeEntityName will generate the table SOME_ENTITY_NAME when CASE_UPPER +or some_entity_name using CASE_LOWER is given. + + +Naming strategy interface +------------------------- +The interface ``Doctrine\ORM\Mapping\NamingStrategy`` allows you to specify +a "naming standard" for database tables and columns. + +.. code-block:: php + + referenceColumnName(); + } + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } + } + +Configuring the namingstrategy is easy if. +Just set your naming strategy calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` :. + +.. code-block:: php + + setNamingStrategy($namingStrategy); diff --git a/vendor/doctrine/orm/docs/en/reference/native-sql.rst b/vendor/doctrine/orm/docs/en/reference/native-sql.rst new file mode 100644 index 00000000..89eb2850 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/native-sql.rst @@ -0,0 +1,905 @@ +Native SQL +========== + +With ``NativeQuery`` you can execute native SELECT SQL statements +and map the results to Doctrine entities or any other result format +supported by Doctrine. + +In order to make this mapping possible, you need to describe +to Doctrine what columns in the result map to which entity property. +This description is represented by a ``ResultSetMapping`` object. + +With this feature you can map arbitrary SQL code to objects, such as highly +vendor-optimized SQL or stored-procedures. + +Writing ``ResultSetMapping`` from scratch is complex, but there is a convenience +wrapper around it called a ``ResultSetMappingBuilder``. It can generate +the mappings for you based on Entities and even generates the ``SELECT`` +clause based on this information for you. + +.. note:: + + If you want to execute DELETE, UPDATE or INSERT statements + the Native SQL API cannot be used and will probably throw errors. + Use ``EntityManager#getConnection()`` to access the native database + connection and call the ``executeUpdate()`` method for these + queries. + +The NativeQuery class +--------------------- + +To create a ``NativeQuery`` you use the method +``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in +the signature of this method, it expects 2 ingredients: The SQL you want to +execute and the ``ResultSetMapping`` that describes how the results will be +mapped. + +Once you obtained an instance of a ``NativeQuery``, you can bind parameters to +it with the same API that ``Query`` has and execute it. + +.. code-block:: php + + createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +ResultSetMappingBuilder +----------------------- + +An easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object. +This has several benefits: + +- The builder takes care of automatically updating your ``ResultSetMapping`` + when the fields or associations change on the metadata of an entity. +- You can generate the required ``SELECT`` expression for a builder + by converting it to a string. +- The API is much simpler than the usual ``ResultSetMapping`` API. + +One downside is that the builder API does not yet support entities +with inheritance hierachies. + +.. code-block:: php + + addRootEntityFromClassMetadata('MyProject\User', 'u'); + $rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id')); + +The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well. + +..versionadded:: 2.4 + +Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause +from a ``ResultSetMappingBuilder``. You can either cast the builder +object to ``(string)`` and the DQL aliases are used as SQL table aliases +or use the ``generateSelectClause($tableAliases)`` method and pass +a mapping from DQL alias (key) to SQL alias (value) + +.. code-block:: php + + generateSelectClause(array( + 'u' => 't1', + 'g' => 't2' + )); + $sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id"; + + +The ResultSetMapping +-------------------- + +Understanding the ``ResultSetMapping`` is the key to using a +``NativeQuery``. A Doctrine result can contain the following +components: + + +- Entity results. These represent root result elements. +- Joined entity results. These represent joined entities in + associations of root entity results. +- Field results. These represent a column in the result set that + maps to a field of an entity. A field result always belongs to an + entity result or joined entity result. +- Scalar results. These represent scalar values in the result set + that will appear in each result row. Adding scalar results to a + ResultSetMapping can also cause the overall result to become + **mixed** (see DQL - Doctrine Query Language) if the same + ResultSetMapping also contains entity results. +- Meta results. These represent columns that contain + meta-information, such as foreign keys and discriminator columns. + When querying for objects (``getResult()``), all meta columns of + root entities or joined entities must be present in the SQL query + and mapped accordingly using ``ResultSetMapping#addMetaResult``. + +.. note:: + + It might not surprise you that Doctrine uses + ``ResultSetMapping`` internally when you create DQL queries. As + the query gets parsed and transformed to SQL, Doctrine fills a + ``ResultSetMapping`` that describes how the results should be + processed by the hydration routines. + + +We will now look at each of the result types that can appear in a +ResultSetMapping in detail. + +Entity results +~~~~~~~~~~~~~~ + +An entity result describes an entity type that appears as a root +element in the transformed result. You add an entity result through +``ResultSetMapping#addEntityResult()``. Let's take a look at the +method signature in detail: + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + + $query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +The result would look like this: + +.. code-block:: php + + array( + [0] => User (Object) + ) + +Note that this would be a partial object if the entity has more +fields than just id and name. In the example above the column and +field names are identical but that is not necessary, of course. +Also note that the query string passed to createNativeQuery is +**real native SQL**. Doctrine does not touch this SQL in any way. + +In the previous basic example, a User had no relations and the +table the class is mapped to owns no foreign keys. The next example +assumes User has a unidirectional or bidirectional one-to-one +association to a CmsAddress, where the User is the owning side and +thus owns the foreign key. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'address_id', 'address_id'); + + $query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Foreign keys are used by Doctrine for lazy-loading purposes when +querying for objects. In the previous example, each user object in +the result will have a proxy (a "ghost") in place of the address +that contains the address\_id. When the ghost proxy is accessed, it +loads itself based on this key. + +Consequently, associations that are *fetch-joined* do not require +the foreign keys to be present in the SQL result set, only +associations that are lazy. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addJoinedEntityResult('Address' , 'a', 'u', 'address'); + $rsm->addFieldResult('a', 'address_id', 'id'); + $rsm->addFieldResult('a', 'street', 'street'); + $rsm->addFieldResult('a', 'city', 'city'); + + $sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' . + 'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?'; + $query = $this->_em->createNativeQuery($sql, $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +In this case the nested entity ``Address`` is registered with the +``ResultSetMapping#addJoinedEntityResult`` method, which notifies +Doctrine that this entity is not hydrated at the root level, but as +a joined entity somewhere inside the object graph. In this case we +specify the alias 'u' as third parameter and ``address`` as fourth +parameter, which means the ``Address`` is hydrated into the +``User::$address`` property. + +If a fetched entity is part of a mapped hierarchy that requires a +discriminator column, this column must be present in the result set +as a meta column so that Doctrine can create the appropriate +concrete type. This is shown in the following example where we +assume that there are one or more subclasses that extend User and +either Class Table Inheritance or Single Table Inheritance is used +to map the hierarchy (both use a discriminator column). + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column + $rsm->setDiscriminatorColumn('u', 'discr'); + + $query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Note that in the case of Class Table Inheritance, an example as +above would result in partial objects if any objects in the result +are actually a subtype of User. When using DQL, Doctrine +automatically includes the necessary joins for this mapping +strategy but with native SQL it is your responsibility. + +Named Native Query +------------------ + +You can also map a native query using a named native query mapping. + +To achieve that, you must describe the SQL resultset structure +using named native query (and sql resultset mappings if is a several resultset mappings). + +Like named query, a named native query can be defined at class level or in a XML or YAML file. + + +A resultSetMapping parameter is defined in @NamedNativeQuery, +it represents the name of a defined @SqlResultSetMapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchMultipleJoinsEntityResults: + name: fetchMultipleJoinsEntityResults + resultSetMapping: mappingMultipleJoinsEntityResults + query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + sqlResultSetMappings: + mappingMultipleJoinsEntityResults: + name: mappingMultipleJoinsEntityResults + columnResult: + 0: + name: numphones + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + column: u_id + 1: + name: name + column: u_name + 2: + name: status + column: u_status + 1: + entityClass: Address + fieldResult: + 0: + name: id + column: a_id + 1: + name: zip + column: a_zip + 2: + name: country + column: a_country + + +Things to note: + - The resultset mapping declares the entities retrieved by this native query. + - Each field of the entity is bound to a SQL alias (or column name). + - All fields of the entity including the ones of subclasses + and the foreign key columns of related entities have to be present in the SQL query. + - Field definitions are optional provided that they map to the same + column name as the one declared on the class property. + - ``__CLASS__`` is an alias for the mapped class + + +In the above example, +the ``fetchJoinedAddress`` named query use the joinMapping result set mapping. +This mapping returns 2 entities, User and Address, each property is declared and associated to a column name, +actually the column name retrieved by the query. + +Let's now see an implicit declaration of the property / column. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + resultSetMapping: mappingFindAll + query: SELECT * FROM addresses + sqlResultSetMappings: + mappingFindAll: + name: mappingFindAll + entityResult: + address: + entityClass: Address + + +In this example, we only describe the entity member of the result set mapping. +The property / column mappings is done using the entity mapping values. +In this case the model property is bound to the model_txt column. +If the association to a related entity involve a composite primary key, +a @FieldResult element should be used for each foreign key column. +The @FieldResult name is composed of the property name for the relationship, +followed by a dot ("."), followed by the name or the field or property of the primary key. + + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchJoinedAddress: + name: fetchJoinedAddress + resultSetMapping: mappingJoinedAddress + query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + sqlResultSetMappings: + mappingJoinedAddress: + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + 3: + name: address.id + column: a_id + 4: + name: address.zip + column: a_zip + 5: + name: address.city + column: a_city + 6: + name: address.country + column: a_country + + + +If you retrieve a single entity and if you use the default mapping, +you can use the resultClass attribute instead of resultSetMapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses WHERE id = ? + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + name: findAll + resultClass: Address + query: SELECT * FROM addresses + + +In some of your native queries, you'll have to return scalar values, +for example when building report queries. +You can map them in the @SqlResultsetMapping through @ColumnResult. +You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though). + +.. configuration-block:: + + .. code-block:: php + + + + + SELECT COUNT(*) AS count FROM addresses + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + count: + name: count + resultSetMapping: mappingCount + query: SELECT COUNT(*) AS count FROM addresses + sqlResultSetMappings: + mappingCount: + name: mappingCount + columnResult: + count: + name: count diff --git a/vendor/doctrine/orm/docs/en/reference/partial-objects.rst b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst new file mode 100644 index 00000000..396eecce --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst @@ -0,0 +1,90 @@ +Partial Objects +=============== + +A partial object is an object whose state is not fully initialized +after being reconstituted from the database and that is +disconnected from the rest of its data. The following section will +describe why partial objects are problematic and what the approach +of Doctrine2 to this problem is. + +.. note:: + + The partial object problem in general does not apply to + methods or queries where you do not retrieve the query result as + objects. Examples are: ``Query#getArrayResult()``, + ``Query#getScalarResult()``, ``Query#getSingleScalarResult()``, + etc. + +.. warning:: + + Use of partial objects is tricky. Fields that are not retrieved + from the database will not be updated by the UnitOfWork even if they + get changed in your objects. You can only promote a partial object + to a fully-loaded object by calling ``EntityManager#refresh()`` + or a DQL query with the refresh flag. + + +What is the problem? +-------------------- + +In short, partial objects are problematic because they are usually +objects with broken invariants. As such, code that uses these +partial objects tends to be very fragile and either needs to "know" +which fields or methods can be safely accessed or add checks around +every field access or method invocation. The same holds true for +the internals, i.e. the method implementations, of such objects. +You usually simply assume the state you need in the method is +available, after all you properly constructed this object before +you pushed it into the database, right? These blind assumptions can +quickly lead to null reference errors when working with such +partial objects. + +It gets worse with the scenario of an optional association (0..1 to +1). When the associated field is NULL, you don't know whether this +object does not have an associated object or whether it was simply +not loaded when the owning object was loaded from the database. + +These are reasons why many ORMs do not allow partial objects at all +and instead you always have to load an object with all its fields +(associations being proxied). One secure way to allow partial +objects is if the programming language/platform allows the ORM tool +to hook deeply into the object and instrument it in such a way that +individual fields (not only associations) can be loaded lazily on +first access. This is possible in Java, for example, through +bytecode instrumentation. In PHP though this is not possible, so +there is no way to have "secure" partial objects in an ORM with +transparent persistence. + +Doctrine, by default, does not allow partial objects. That means, +any query that only selects partial object data and wants to +retrieve the result as objects (i.e. ``Query#getResult()``) will +raise an exception telling you that partial objects are dangerous. +If you want to force a query to return you partial objects, +possibly as a performance tweak, you can use the ``partial`` +keyword as follows: + +.. code-block:: php + + createQuery("select partial u.{id,name} from MyApp\Domain\User u"); + +You can also get a partial reference instead of a proxy reference by +calling: + +.. code-block:: php + + getPartialReference('MyApp\Domain\User', 1); + +Partial references are objects with only the identifiers set as they +are passed to the second argument of the ``getPartialReference()`` method. +All other fields are null. + +When should I force partial objects? +------------------------------------ + +Mainly for optimization purposes, but be careful of premature +optimization as partial objects lead to potentially more fragile +code. + + diff --git a/vendor/doctrine/orm/docs/en/reference/php-mapping.rst b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst new file mode 100644 index 00000000..90f00d9b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst @@ -0,0 +1,310 @@ +PHP Mapping +=========== + +Doctrine 2 also allows you to provide the ORM metadata in the form +of plain PHP code using the ``ClassMetadata`` API. You can write +the code in PHP files or inside of a static function named +``loadMetadata($class)`` on the entity class itself. + +PHP Files +--------- + +If you wish to write your mapping information inside PHP files that +are named after the entity and included to populate the metadata +for an entity you can do so by using the ``PHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now imagine we had an entity named ``Entities\User`` and we wanted +to write a mapping file for it using the above configured +``PHPDriver`` instance: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + +Now we can easily retrieve the populated ``ClassMetadata`` instance +where the ``PHPDriver`` includes the file and the +``ClassMetadataFactory`` caches it for later retrieval: + +.. code-block:: php + + getClassMetadata('Entities\User'); + // or + $class = $em->getMetadataFactory()->getMetadataFor('Entities\User'); + +Static Function +--------------- + +In addition to the PHP files you can also specify your mapping +information inside of a static function defined on the entity class +itself. This is useful for cases where you want to keep your entity +and mapping information together but don't want to use annotations. +For this you just need to use the ``StaticPHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now you just need to define a static function named +``loadMetadata($metadata)`` on your entity: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + } + } + +ClassMetadataBuilder +-------------------- + +To ease the use of the ClassMetadata API (which is very raw) there is a ``ClassMetadataBuilder`` that you can use. + +.. code-block:: php + + createField('id', 'integer')->isPrimaryKey()->generatedValue()->build(); + $builder->addField('username', 'string'); + } + } + +The API of the ClassMetadataBuilder has the following methods with a fluent interface: + +- ``addField($name, $type, array $mapping)`` +- ``setMappedSuperclass()`` +- ``setReadOnly()`` +- ``setCustomRepositoryClass($className)`` +- ``setTable($name)`` +- ``addIndex(array $columns, $indexName)`` +- ``addUniqueConstraint(array $columns, $constraintName)`` +- ``addNamedQuery($name, $dqlQuery)`` +- ``setJoinedTableInheritance()`` +- ``setSingleTableInheritance()`` +- ``setDiscriminatorColumn($name, $type = 'string', $length = 255)`` +- ``addDiscriminatorMapClass($name, $class)`` +- ``setChangeTrackingPolicyDeferredExplicit()`` +- ``setChangeTrackingPolicyNotify()`` +- ``addLifecycleEvent($methodName, $event)`` +- ``addManyToOne($name, $targetEntity, $inversedBy = null)`` +- ``addInverseOneToOne($name, $targetEntity, $mappedBy)`` +- ``addOwningOneToOne($name, $targetEntity, $inversedBy = null)`` +- ``addOwningManyToMany($name, $targetEntity, $inversedBy = null)`` +- ``addInverseManyToMany($name, $targetEntity, $mappedBy)`` +- ``addOneToMany($name, $targetEntity, $mappedBy)`` + +It also has several methods that create builders (which are necessary for advanced mappings): + +- ``createField($name, $type)`` returns a ``FieldBuilder`` instance +- ``createManyToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createOneToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance +- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance + +ClassMetadataInfo API +--------------------- + +The ``ClassMetadataInfo`` class is the base data object for storing +the mapping metadata for a single entity. It contains all the +getters and setters you need populate and retrieve information for +an entity. + +General Setters +~~~~~~~~~~~~~~~ + + +- ``setTableName($tableName)`` +- ``setPrimaryTable(array $primaryTableDefinition)`` +- ``setCustomRepositoryClass($repositoryClassName)`` +- ``setIdGeneratorType($generatorType)`` +- ``setIdGenerator($generator)`` +- ``setSequenceGeneratorDefinition(array $definition)`` +- ``setChangeTrackingPolicy($policy)`` +- ``setIdentifier(array $identifier)`` + +Inheritance Setters +~~~~~~~~~~~~~~~~~~~ + + +- ``setInheritanceType($type)`` +- ``setSubclasses(array $subclasses)`` +- ``setParentClasses(array $classNames)`` +- ``setDiscriminatorColumn($columnDef)`` +- ``setDiscriminatorMap(array $map)`` + +Field Mapping Setters +~~~~~~~~~~~~~~~~~~~~~ + + +- ``mapField(array $mapping)`` +- ``mapOneToOne(array $mapping)`` +- ``mapOneToMany(array $mapping)`` +- ``mapManyToOne(array $mapping)`` +- ``mapManyToMany(array $mapping)`` + +Lifecycle Callback Setters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``addLifecycleCallback($callback, $event)`` +- ``setLifecycleCallbacks(array $callbacks)`` + +Versioning Setters +~~~~~~~~~~~~~~~~~~ + + +- ``setVersionMapping(array &$mapping)`` +- ``setVersioned($bool)`` +- ``setVersionField()`` + +General Getters +~~~~~~~~~~~~~~~ + + +- ``getTableName()`` +- ``getTemporaryIdTableName()`` + +Identifier Getters +~~~~~~~~~~~~~~~~~~ + + +- ``getIdentifierColumnNames()`` +- ``usesIdGenerator()`` +- ``isIdentifier($fieldName)`` +- ``isIdGeneratorIdentity()`` +- ``isIdGeneratorSequence()`` +- ``isIdGeneratorTable()`` +- ``isIdentifierNatural()`` +- ``getIdentifierFieldNames()`` +- ``getSingleIdentifierFieldName()`` +- ``getSingleIdentifierColumnName()`` + +Inheritance Getters +~~~~~~~~~~~~~~~~~~~ + + +- ``isInheritanceTypeNone()`` +- ``isInheritanceTypeJoined()`` +- ``isInheritanceTypeSingleTable()`` +- ``isInheritanceTypeTablePerClass()`` +- ``isInheritedField($fieldName)`` +- ``isInheritedAssociation($fieldName)`` + +Change Tracking Getters +~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isChangeTrackingDeferredExplicit()`` +- ``isChangeTrackingDeferredImplicit()`` +- ``isChangeTrackingNotify()`` + +Field & Association Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isUniqueField($fieldName)`` +- ``isNullable($fieldName)`` +- ``getColumnName($fieldName)`` +- ``getFieldMapping($fieldName)`` +- ``getAssociationMapping($fieldName)`` +- ``getAssociationMappings()`` +- ``getFieldName($columnName)`` +- ``hasField($fieldName)`` +- ``getColumnNames(array $fieldNames = null)`` +- ``getTypeOfField($fieldName)`` +- ``getTypeOfColumn($columnName)`` +- ``hasAssociation($fieldName)`` +- ``isSingleValuedAssociation($fieldName)`` +- ``isCollectionValuedAssociation($fieldName)`` + +Lifecycle Callback Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``hasLifecycleCallbacks($lifecycleEvent)`` +- ``getLifecycleCallbacks($event)`` + +ClassMetadata API +----------------- + +The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds +the runtime functionality required by Doctrine. It adds a few extra +methods related to runtime reflection for working with the entities +themselves. + + +- ``getReflectionClass()`` +- ``getReflectionProperties()`` +- ``getReflectionProperty($name)`` +- ``getSingleIdReflectionProperty()`` +- ``getIdentifierValues($entity)`` +- ``setIdentifierValues($entity, $id)`` +- ``setFieldValue($entity, $field, $value)`` +- ``getFieldValue($entity, $field)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/query-builder.rst b/vendor/doctrine/orm/docs/en/reference/query-builder.rst new file mode 100644 index 00000000..8029822b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/query-builder.rst @@ -0,0 +1,541 @@ +The QueryBuilder +================ + +A ``QueryBuilder`` provides an API that is designed for +conditionally constructing a DQL query in several steps. + +It provides a set of classes and methods that is able to +programmatically build queries, and also provides a fluent API. +This means that you can change between one methodology to the other +as you want, and also pick one if you prefer. + +Constructing a new QueryBuilder object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The same way you build a normal Query, you build a ``QueryBuilder`` +object, just providing the correct method name. Here is an example +how to build a ``QueryBuilder`` object: + +.. code-block:: php + + createQueryBuilder(); + +Once you have created an instance of QueryBuilder, it provides a +set of useful informative functions that you can use. One good +example is to inspect what type of object the ``QueryBuilder`` is. + +.. code-block:: php + + getType(); // Prints: 0 + +There're currently 3 possible return values for ``getType()``: + + +- ``QueryBuilder::SELECT``, which returns value 0 +- ``QueryBuilder::DELETE``, returning value 1 +- ``QueryBuilder::UPDATE``, which returns value 2 + +It is possible to retrieve the associated ``EntityManager`` of the +current ``QueryBuilder``, its DQL and also a ``Query`` object when +you finish building your DQL. + +.. code-block:: php + + getEntityManager(); + + // example4: retrieve the DQL string of what was defined in QueryBuilder + $dql = $qb->getDql(); + + // example5: retrieve the associated Query object with the processed DQL + $q = $qb->getQuery(); + +Internally, ``QueryBuilder`` works with a DQL cache to increase +performance. Any changes that may affect the generated DQL actually +modifies the state of ``QueryBuilder`` to a stage we call +STATE\_DIRTY. One ``QueryBuilder`` can be in two different states: + + +- ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been + altered since last retrieval or nothing were added since its + instantiation +- ``QueryBuilder::STATE_DIRTY``, means DQL query must (and will) + be processed on next retrieval + +Working with QueryBuilder +~~~~~~~~~~~~~~~~~~~~~~~~~ + + +High level API methods +^^^^^^^^^^^^^^^^^^^^^^ + +To simplify even more the way you build a query in Doctrine, we can take +advantage of what we call Helper methods. For all base code, there +is a set of useful methods to simplify a programmer's life. To +illustrate how to work with them, here is the same example 6 +re-written using ``QueryBuilder`` helper methods: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC'); + +``QueryBuilder`` helper methods are considered the standard way to +build DQL queries. Although it is supported, it should be avoided +to use string based queries and greatly encouraged to use +``$qb->expr()->*`` methods. Here is a converted example 8 to +suggested standard way to build queries: + +.. code-block:: php + + select(array('u')) // string 'u' is converted to array internally + ->from('User', 'u') + ->where($qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->orderBy('u.surname', 'ASC')); + +Here is a complete list of helper methods available in ``QueryBuilder``: + +.. code-block:: php + + select('u') + // Example - $qb->select(array('u', 'p')) + // Example - $qb->select($qb->expr()->select('u', 'p')) + public function select($select = null); + + // Example - $qb->delete('User', 'u') + public function delete($delete = null, $alias = null); + + // Example - $qb->update('Group', 'g') + public function update($update = null, $alias = null); + + // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold')) + // Example - $qb->set('u.numChilds', 'u.numChilds + ?1') + // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1')) + public function set($key, $value); + + // Example - $qb->from('Phonenumber', 'p') + public function from($from, $alias = null); + + // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1')) + // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1') + public function innerJoin($join, $alias = null, $conditionType = null, $condition = null); + + // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55)) + // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55') + public function leftJoin($join, $alias = null, $conditionType = null, $condition = null); + + // NOTE: ->where() overrides all previously set conditions + // + // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2')) + // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2'))) + // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2') + public function where($where); + + // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0')) + public function andWhere($where); + + // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10)); + public function orWhere($where); + + // NOTE: -> groupBy() overrides all previously set grouping conditions + // + // Example - $qb->groupBy('u.id') + public function groupBy($groupBy); + + // Example - $qb->addGroupBy('g.name') + public function addGroupBy($groupBy); + + // NOTE: -> having() overrides all previously set having conditions + // + // Example - $qb->having('u.salary >= ?1') + // Example - $qb->having($qb->expr()->gte('u.salary', '?1')) + public function having($having); + + // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0)) + public function andHaving($having); + + // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100')) + public function orHaving($having); + + // NOTE: -> orderBy() overrides all previously set ordering conditions + // + // Example - $qb->orderBy('u.surname', 'DESC') + public function orderBy($sort, $order = null); + + // Example - $qb->addOrderBy('u.firstName') + public function addOrderBy($sort, $order = null); // Default $order = 'ASC' + } + +Binding parameters to your query +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Doctrine supports dynamic binding of parameters to your query, +similar to preparing queries. You can use both strings and numbers +as placeholders, although both have a slightly different syntax. +Additionally, you must make your choice: Mixing both styles is not +allowed. Binding parameters can simply be achieved as follows: + +.. code-block:: php + + select('u') + ->from('User u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC'); + ->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100 + +You are not forced to enumerate your placeholders as the +alternative syntax is available: + +.. code-block:: php + + select('u') + ->from('User u') + ->where('u.id = :identifier') + ->orderBy('u.name', 'ASC'); + ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100 + +Note that numeric placeholders start with a ? followed by a number +while the named placeholders start with a : followed by a string. + +Calling ``setParameter()`` automatically infers which type you are setting as +value. This works for integers, arrays of strings/integers, DateTime instances +and for managed entities. If you want to set a type explicitly you can call +the third argument to ``setParameter()`` explicitly. It accepts either a PDO +type or a DBAL Type name for conversion. + +If you've got several parameters to bind to your query, you can +also use setParameters() instead of setParameter() with the +following syntax: + +.. code-block:: php + + setParameters(array(1 => 'value for ?1', 2 => 'value for ?2')); + +Getting already bound parameters is easy - simply use the above +mentioned syntax with "getParameter()" or "getParameters()": + +.. code-block:: php + + getParameters(); + // $params instanceof \Doctrine\Common\Collections\ArrayCollection + + // Equivalent to + $param = $qb->getParameter(1); + // $param instanceof \Doctrine\ORM\Query\Parameter + +Note: If you try to get a parameter that was not bound yet, +getParameter() simply returns NULL. + +The API of a Query Parameter is: + +.. code-block:: php + + namespace Doctrine\ORM\Query; + + class Parameter + { + public function getName(); + public function getValue(); + public function getType(); + public function setValue($value, $type = null); + } + +Limiting the Result +^^^^^^^^^^^^^^^^^^^ + +To limit a result the query builder has some methods in common with +the Query object which can be retrieved from ``EntityManager#createQuery()``. + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('orderBy', 'u.name ASC') + ->setFirstResult( $offset ) + ->setMaxResults( $limit ); + +Executing a Query +^^^^^^^^^^^^^^^^^ + +The QueryBuilder is a builder object only, it has no means of actually +executing the Query. Additionally a set of parameters such as query hints +cannot be set on the QueryBuilder itself. This is why you always have to convert +a querybuilder instance into a Query object: + +.. code-block:: php + + getQuery(); + + // Set additional Query options + $query->setQueryHint('foo', 'bar'); + $query->useResultCache('my_cache_id'); + + // Execute Query + $result = $query->getResult(); + $single = $query->getSingleResult(); + $array = $query->getArrayResult(); + $scalar = $query->getScalarResult(); + $singleScalar = $query->getSingleScalarResult(); + +The Expr class +^^^^^^^^^^^^^^ + +To workaround some of the issues that ``add()`` method may cause, +Doctrine created a class that can be considered as a helper for +building expressions. This class is called ``Expr``, which provides a +set of useful methods to help build expressions: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', $qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Although it still sounds complex, the ability to programmatically +create conditions are the main feature of ``Expr``. Here it is a +complete list of supported helper methods available: + +.. code-block:: php + + expr()->andX($cond1 [, $condN])->add(...)->... + public function andX($x = null); // Returns Expr\AndX instance + + // Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->... + public function orX($x = null); // Returns Expr\OrX instance + + + /** Comparison objects **/ + + // Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1 + public function eq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->neq('u.id', '?1') => u.id <> ?1 + public function neq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lt('u.id', '?1') => u.id < ?1 + public function lt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lte('u.id', '?1') => u.id <= ?1 + public function lte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gt('u.id', '?1') => u.id > ?1 + public function gt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1 + public function gte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->isNull('u.id') => u.id IS NULL + public function isNull($x); // Returns string + + // Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL + public function isNotNull($x); // Returns string + + + /** Arithmetic objects **/ + + // Example - $qb->expr()->prod('u.id', '2') => u.id * 2 + public function prod($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->diff('u.id', '2') => u.id - 2 + public function diff($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->sum('u.id', '2') => u.id + 2 + public function sum($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->quot('u.id', '2') => u.id / 2 + public function quot($x, $y); // Returns Expr\Math instance + + + /** Pseudo-function objects **/ + + // Example - $qb->expr()->exists($qb2->getDql()) + public function exists($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->all($qb2->getDql()) + public function all($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->some($qb2->getDql()) + public function some($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->any($qb2->getDql()) + public function any($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->not($qb->expr()->eq('u.id', '?1')) + public function not($restriction); // Returns Expr\Func instance + + // Example - $qb->expr()->in('u.id', array(1, 2, 3)) + // Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. + // Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above) + public function in($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->notIn('u.id', '2') + public function notIn($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%')) + public function like($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->between('u.id', '1', '10') + public function between($val, $x, $y); // Returns Expr\Func + + + /** Function objects **/ + + // Example - $qb->expr()->trim('u.firstname') + public function trim($x); // Returns Expr\Func + + // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname')) + public function concat($x, $y); // Returns Expr\Func + + // Example - $qb->expr()->substr('u.firstname', 0, 1) + public function substr($x, $from, $len); // Returns Expr\Func + + // Example - $qb->expr()->lower('u.firstname') + public function lower($x); // Returns Expr\Func + + // Example - $qb->expr()->upper('u.firstname') + public function upper($x); // Returns Expr\Func + + // Example - $qb->expr()->length('u.firstname') + public function length($x); // Returns Expr\Func + + // Example - $qb->expr()->avg('u.age') + public function avg($x); // Returns Expr\Func + + // Example - $qb->expr()->max('u.age') + public function max($x); // Returns Expr\Func + + // Example - $qb->expr()->min('u.age') + public function min($x); // Returns Expr\Func + + // Example - $qb->expr()->abs('u.currentBalance') + public function abs($x); // Returns Expr\Func + + // Example - $qb->expr()->sqrt('u.currentBalance') + public function sqrt($x); // Returns Expr\Func + + // Example - $qb->expr()->count('u.firstname') + public function count($x); // Returns Expr\Func + + // Example - $qb->expr()->countDistinct('u.surname') + public function countDistinct($x); // Returns Expr\Func + } + + +Low Level API +^^^^^^^^^^^^^ + +Now we have describe the low level (thought of as the +hardcore method) of creating queries. It may be useful to work at +this level for optimization purposes, but most of the time it is +preferred to work at a higher level of abstraction. + +All helper methods in ``QueryBuilder`` actually rely on a single +one: ``add()``. This method is responsible of building every piece +of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and +``$append`` (default=false) + + +- ``$dqlPartName``: Where the ``$dqlPart`` should be placed. + Possible values: select, from, where, groupBy, having, orderBy +- ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts + a string or any instance of ``Doctrine\ORM\Query\Expr\*`` +- ``$append``: Optional flag (default=false) if the ``$dqlPart`` + should override all previously defined items in ``$dqlPartName`` or + not (no effect on the ``where`` and ``having`` DQL query parts, + which always override all previously defined items) + +- + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('where', 'u.id = ?1') + ->add('orderBy', 'u.name ASC'); + +Expr\* classes +^^^^^^^^^^^^^^ + +When you call ``add()`` with string, it internally evaluates to an +instance of ``Doctrine\ORM\Query\Expr\Expr\*`` class. Here is the +same query of example 6 written using +``Doctrine\ORM\Query\Expr\Expr\*`` classes: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', new Expr\Comparison('u.id', '=', '?1')) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Of course this is the hardest way to build a DQL query in Doctrine. +To simplify some of these efforts, we introduce what we call as +``Expr`` helper class. + diff --git a/vendor/doctrine/orm/docs/en/reference/tools.rst b/vendor/doctrine/orm/docs/en/reference/tools.rst new file mode 100644 index 00000000..a5f99d14 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/tools.rst @@ -0,0 +1,509 @@ +Tools +===== + +Doctrine Console +---------------- + +The Doctrine Console is a Command Line Interface tool for simplifying common +administration tasks during the development of a project that uses Doctrine 2. + +Take a look at the :doc:`Installation and Configuration ` +chapter for more information how to setup the console command. + +Display Help Information +~~~~~~~~~~~~~~~~~~~~~~~~ + +Type ``php vendor/bin/doctrine`` on the command line and you should see an +overview of the available commands or use the --help flag to get +information on the available commands. If you want to know more +about the use of generate entities for example, you can call: + +.. code-block:: php + + $> php vendor/bin/doctrine orm:generate-entities --help + + +Configuration +~~~~~~~~~~~~~ + +Whenever the ``doctrine`` command line tool is invoked, it can +access all Commands that were registered by developer. There is no +auto-detection mechanism at work. The Doctrine binary +already registers all the commands that currently ship with +Doctrine DBAL and ORM. If you want to use additional commands you +have to register them yourself. + +All the commands of the Doctrine Console require access to the EntityManager +or DBAL Connection. You have to inject them into the console application +using so called Helper-Sets. This requires either the ``db`` +or the ``em`` helpers to be defined in order to work correctly. + +Whenever you invoke the Doctrine binary the current folder is searched for a +``cli-config.php`` file. This file contains the project specific configuration: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn) + )); + $cli->setHelperSet($helperSet); + +When dealing with the ORM package, the EntityManagerHelper is +required: + +.. code-block:: php + + new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + $cli->setHelperSet($helperSet); + +The HelperSet instance has to be generated in a separate file (i.e. +``cli-config.php``) that contains typical Doctrine bootstrap code +and predefines the needed HelperSet attributes mentioned above. A +sample ``cli-config.php`` file looks as follows: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + +It is important to define a correct HelperSet that Doctrine binary +script will ultimately use. The Doctrine Binary will automatically +find the first instance of HelperSet in the global variable +namespace and use this. + +.. note:: + + You have to adjust this snippet for your specific application or framework + and use their facilities to access the Doctrine EntityManager and + Connection Resources. + +Command Overview +~~~~~~~~~~~~~~~~ + +The following Commands are currently available: + + +- ``help`` Displays help for a command (?) +- ``list`` Lists commands +- ``dbal:import`` Import SQL file(s) directly to Database. +- ``dbal:run-sql`` Executes arbitrary SQL directly from the + command line. +- ``orm:clear-cache:metadata`` Clear all metadata cache of the + various cache drivers. +- ``orm:clear-cache:query`` Clear all query cache of the various + cache drivers. +- ``orm:clear-cache:result`` Clear result cache of the various + cache drivers. +- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a + Doctrine 2.X schema. +- ``orm:convert-mapping`` Convert mapping information between + supported formats. +- ``orm:ensure-production-settings`` Verify that Doctrine is + properly configured for a production environment. +- ``orm:generate-entities`` Generate entity classes and method + stubs from your mapping information. +- ``orm:generate-proxies`` Generates proxy classes for entity + classes. +- ``orm:generate-repositories`` Generate repository classes from + your mapping information. +- ``orm:run-dql`` Executes arbitrary DQL directly from the command + line. +- ``orm:schema-tool:create`` Processes the schema and either + create it directly on EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:drop`` Processes the schema and either drop + the database schema of EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:update`` Processes the schema and either + update the database schema of EntityManager Storage Connection or + generate the SQL output. + +For these commands are also available aliases: + + +- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``. +- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``. +- ``orm:generate:entities`` is alias for ``orm:generate-entities``. +- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``. +- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``. + +.. note:: + + Console also supports auto completion, for example, instead of + ``orm:clear-cache:query`` you can use just ``o:c:q``. + +Database Schema Generation +-------------------------- + +.. note:: + + SchemaTool can do harm to your database. It will drop or alter + tables, indexes, sequences and such. Please use this tool with + caution in development and not on a production server. It is meant + for helping you develop your Database Schema, but NOT with + migrating schema from A to B in production. A safe approach would + be generating the SQL on development server and saving it into SQL + Migration files that are executed manually on the production + server. + + SchemaTool assumes your Doctrine Project uses the given database on + its own. Update and Drop commands will mess with other tables if + they are not related to the current project that is using Doctrine. + Please be careful! + + +To generate your database schema from your Doctrine mapping files +you can use the ``SchemaTool`` class or the ``schema-tool`` Console +Command. + +When using the SchemaTool class directly, create your schema using +the ``createSchema()`` method. First create an instance of the +``SchemaTool`` and pass it an instance of the ``EntityManager`` +that you want to use to create the schema. This method receives an +array of ``ClassMetadataInfo`` instances. + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $tool->createSchema($classes); + +To drop the schema you can use the ``dropSchema()`` method. + +.. code-block:: php + + dropSchema($classes); + +This drops all the tables that are currently used by your metadata +model. When you are changing your metadata a lot during development +you might want to drop the complete database instead of only the +tables of the current model to clean up with orphaned tables. + +.. code-block:: php + + dropSchema($classes, \Doctrine\ORM\Tools\SchemaTool::DROP_DATABASE); + +You can also use database introspection to update your schema +easily with the ``updateSchema()`` method. It will compare your +existing database schema to the passed array of +``ClassMetdataInfo`` instances. + +.. code-block:: php + + updateSchema($classes); + +If you want to use this functionality from the command line you can +use the ``schema-tool`` command. + +To create the schema use the ``create`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:create + +To drop the schema use the ``drop`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + +If you want to drop and then recreate the schema then use both +options: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + $ php doctrine orm:schema-tool:create + +As you would think, if you want to update your schema use the +``update`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:update + +All of the above commands also accept a ``--dump-sql`` option that +will output the SQL for the ran operation. + +.. code-block:: php + + $ php doctrine orm:schema-tool:create --dump-sql + +Before using the orm:schema-tool commands, remember to configure +your cli-config.php properly. + +.. note:: + + When using the Annotation Mapping Driver you have to either setup + your autoloader in the cli-config.php correctly to find all the + entities, or you can use the second argument of the + ``EntityManagerHelper`` to specify all the paths of your entities + (or mapping files), i.e. + ``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);`` + +Entity Generation +----------------- + +Generate entity classes and method stubs from your mapping information. + +.. code-block:: php + + $ php doctrine orm:generate-entities + $ php doctrine orm:generate-entities --update-entities + $ php doctrine orm:generate-entities --regenerate-entities + +This command is not suited for constant usage. It is a little helper and does +not support all the mapping edge cases very well. You still have to put work +in your entities after using this command. + +It is possible to use the EntityGenerator on code that you have already written. It will +not be lost. The EntityGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the entity code if you are using entities as Data +Access Objects only and don't put much additional logic on them. If you are +however putting much more logic on the entities you should refrain from using +the entity-generator and code your entities manually. + +.. note:: + + Even if you specified Inheritance options in your + XML or YAML Mapping files the generator cannot generate the base and + child classes for you correctly, because it doesn't know which + class is supposed to extend which. You have to adjust the entity + code manually for inheritance to work! + + +Convert Mapping Information +--------------------------- + +Convert mapping information between supported formats. + +This is an **execute one-time** command. It should not be necessary for +you to call this method multiple times, especially when using the ``--from-database`` +flag. + +Converting an existing database schema into mapping files only solves about 70-80% +of the necessary mapping information. Additionally the detection from an existing +database cannot detect inverse associations, inheritance types, +entities with foreign keys as primary keys and many of the +semantical operations on associations such as cascade. + +.. note:: + + There is no need to convert YAML or XML mapping files to annotations + every time you make changes. All mapping drivers are first class citizens + in Doctrine 2 and can be used as runtime mapping for the ORM. See the + docs on XML and YAML Mapping for an example how to register this metadata + drivers as primary mapping source. + +To convert some mapping information between the various supported +formats you can use the ``ClassMetadataExporter`` to get exporter +instances for the different formats: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + +Now you can export some ``ClassMetadata`` instances: + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $exporter->setMetadata($classes); + $exporter->export(); + +This functionality is also available from the command line to +convert your loaded mapping information to another format. The +``orm:convert-mapping`` command accepts two arguments, the type to +convert to and the path to generate it: + +.. code-block:: php + + $ php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml + +Reverse Engineering +------------------- + +You can use the ``DatabaseDriver`` to reverse engineer a database +to an array of ``ClassMetadataInfo`` instances and generate YAML, +XML, etc. from them. + +.. note:: + + Reverse Engineering is a **one-time** process that can get you started with a project. + Converting an existing database schema into mapping files only detects about 70-80% + of the necessary mapping information. Additionally the detection from an existing + database cannot detect inverse associations, inheritance types, + entities with foreign keys as primary keys and many of the + semantical operations on associations such as cascade. + +First you need to retrieve the metadata instances with the +``DatabaseDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl( + new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( + $em->getConnection()->getSchemaManager() + ) + ); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + +Now you can get an exporter instance and export the loaded metadata +to yml: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + $exporter->setMetadata($metadata); + $exporter->export(); + +You can also reverse engineer a database using the +``orm:convert-mapping`` command: + +.. code-block:: php + + $ php doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml + +.. note:: + + Reverse Engineering is not always working perfectly + depending on special cases. It will only detect Many-To-One + relations (even if they are One-To-One) and will try to create + entities from Many-To-Many tables. It also has problems with naming + of foreign keys that have multiple column names. Any Reverse + Engineered Database-Schema needs considerable manual work to become + a useful domain model. + + +Runtime vs Development Mapping Validation +----------------------------------------- + +For performance reasons Doctrine 2 has to skip some of the +necessary validation of metadata mappings. You have to execute +this validation in your development workflow to verify the +associations are correctly defined. + +You can either use the Doctrine Command Line Tool: + +.. code-block:: php + + doctrine orm:validate-schema + +Or you can trigger the validation manually: + +.. code-block:: php + + validateMapping(); + + if (count($errors) > 0) { + // Lots of errors! + echo implode("\n\n", $errors); + } + +If the mapping is invalid the errors array contains a positive +number of elements with error messages. + +.. warning:: + + One mapping option that is not validated is the use of the referenced column name. + It has to point to the equivalent primary key otherwise Doctrine will not work. + +.. note:: + + One common error is to use a backlash in front of the + fully-qualified class-name. Whenever a FQCN is represented inside a + string (such as in your mapping definitions) you have to drop the + prefix backslash. PHP does this with ``get_class()`` or Reflection + methods for backwards compatibility reasons. + + +Adding own commands +------------------- + +You can also add your own commands on-top of the Doctrine supported +tools if you are using a manually built console script. + +To include a new command on Doctrine Console, you need to do modify the +``doctrine.php`` file a little: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + // Register All Doctrine Commands + ConsoleRunner::addCommands($cli); + + // Register your own command + $cli->addCommand(new \MyProject\Tools\Console\Commands\MyCustomCommand); + + // Runs console application + $cli->run(); + +Additionally, include multiple commands (and overriding previously +defined ones) is possible through the command: + +.. code-block:: php + + addCommands(array( + new \MyProject\Tools\Console\Commands\MyCustomCommand(), + new \MyProject\Tools\Console\Commands\SomethingCommand(), + new \MyProject\Tools\Console\Commands\AnotherCommand(), + new \MyProject\Tools\Console\Commands\OneMoreCommand(), + )); diff --git a/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst new file mode 100644 index 00000000..1b06156e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst @@ -0,0 +1,353 @@ +Transactions and Concurrency +============================ + +Transaction Demarcation +----------------------- + +Transaction demarcation is the task of defining your transaction +boundaries. Proper transaction demarcation is very important +because if not done properly it can negatively affect the +performance of your application. Many databases and database +abstraction layers like PDO by default operate in auto-commit mode, +which means that every single SQL statement is wrapped in a small +transaction. Without any explicit transaction demarcation from your +side, this quickly results in poor performance because transactions +are not cheap. + +For the most part, Doctrine 2 already takes care of proper +transaction demarcation for you: All the write operations +(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()`` +is invoked which wraps all of these changes in a single +transaction. + +However, Doctrine 2 also allows (and encourages) you to take over +and control transaction demarcation yourself. + +These are two ways to deal with transactions when using the +Doctrine ORM and are now described in more detail. + +Approach 1: Implicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The first approach is to use the implicit transaction handling +provided by the Doctrine ORM EntityManager. Given the following +code snippet, without any explicit transaction demarcation: + +.. code-block:: php + + setName('George'); + $em->persist($user); + $em->flush(); + +Since we do not do any custom transaction demarcation in the above +code, ``EntityManager#flush()`` will begin and commit/rollback a +transaction. This behavior is made possible by the aggregation of +the DML operations by the Doctrine ORM and is sufficient if all the +data manipulation that is part of a unit of work happens through +the domain model and thus the ORM. + +Approach 2: Explicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The explicit alternative is to use the ``Doctrine\DBAL\Connection`` +API directly to control the transaction boundaries. The code then +looks like this: + +.. code-block:: php + + getConnection()->beginTransaction(); // suspend auto-commit + try { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + $em->flush(); + $em->getConnection()->commit(); + } catch (Exception $e) { + $em->getConnection()->rollback(); + $em->close(); + throw $e; + } + +Explicit transaction demarcation is required when you want to +include custom DBAL operations in a unit of work or when you want +to make use of some methods of the ``EntityManager`` API that +require an active transaction. Such methods will throw a +``TransactionRequiredException`` to inform you of that +requirement. + +A more convenient alternative for explicit transaction demarcation +is the use of provided control abstractions in the form of +``Connection#transactional($func)`` and +``EntityManager#transactional($func)``. When used, these control +abstractions ensure that you never forget to rollback the +transaction or close the ``EntityManager``, apart from the obvious +code reduction. An example that is functionally equivalent to the +previously shown code looks as follows: + +.. code-block:: php + + transactional(function($em) { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + }); + +The difference between ``Connection#transactional($func)`` and +``EntityManager#transactional($func)`` is that the latter +abstraction flushes the ``EntityManager`` prior to transaction +commit and also closes the ``EntityManager`` properly when an +exception occurs (in addition to rolling back the transaction). + +Exception Handling +~~~~~~~~~~~~~~~~~~ + +When using implicit transaction demarcation and an exception occurs +during ``EntityManager#flush()``, the transaction is automatically +rolled back and the ``EntityManager`` closed. + +When using explicit transaction demarcation and an exception +occurs, the transaction should be rolled back immediately and the +``EntityManager`` closed by invoking ``EntityManager#close()`` and +subsequently discarded, as demonstrated in the example above. This +can be handled elegantly by the control abstractions shown earlier. +Note that when catching ``Exception`` you should generally re-throw +the exception. If you intend to recover from some exceptions, catch +them explicitly in earlier catch blocks (but do not forget to +rollback the transaction and close the ``EntityManager`` there as +well). All other best practices of exception handling apply +similarly (i.e. either log or re-throw, not both, etc.). + +As a result of this procedure, all previously managed or removed +instances of the ``EntityManager`` become detached. The state of +the detached objects will be the state at the point at which the +transaction was rolled back. The state of the objects is in no way +rolled back and thus the objects are now out of synch with the +database. The application can continue to use the detached objects, +knowing that their state is potentially no longer accurate. + +If you intend to start another unit of work after an exception has +occurred you should do that with a new ``EntityManager``. + +Locking Support +--------------- + +Doctrine 2 offers support for Pessimistic- and Optimistic-locking +strategies natively. This allows to take very fine-grained control +over what kind of locking is required for your Entities in your +application. + +Optimistic Locking +~~~~~~~~~~~~~~~~~~ + +Database transactions are fine for concurrency control during a +single request. However, a database transaction should not span +across requests, the so-called "user think time". Therefore a +long-running "business transaction" that spans multiple requests +needs to involve several database transactions. Thus, database +transactions alone can no longer control concurrency during such a +long-running business transaction. Concurrency control becomes the +partial responsibility of the application itself. + +Doctrine has integrated support for automatic optimistic locking +via a version field. In this approach any entity that should be +protected against concurrent modifications during long-running +business transactions gets a version field that is either a simple +number (mapping type: integer) or a timestamp (mapping type: +datetime). When changes to such an entity are persisted at the end +of a long-running conversation the version of the entity is +compared to the version in the database and if they don't match, an +``OptimisticLockException`` is thrown, indicating that the entity +has been modified by someone else already. + +You designate a version field in an entity as follows. In this +example we'll use an integer. + +.. code-block:: php + + find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); + + // do the work + + $em->flush(); + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Or you can use ``EntityManager#lock()`` to find out: + +.. code-block:: php + + find('User', $theEntityId); + + try { + // assert version + $em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion); + + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Important Implementation Notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can easily get the optimistic locking workflow wrong if you +compare the wrong versions. Say you have Alice and Bob editing a +hypothetical blog post: + +- Alice reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob updates the headline to "Bar", upgrading the optimistic lock + version to 2 (POST Request of a Form) +- Alice updates the headline to "Baz", ... (POST Request of a + Form) + +Now at the last stage of this scenario the blog post has to be read +again from the database before Alice's headline can be applied. At +this point you will want to check if the blog post is still at +version 1 (which it is not in this scenario). + +Using optimistic locking correctly, you *have* to add the version +as an additional hidden field (or into the SESSION for more +safety). Otherwise you cannot verify the version is still the one +being originally read from the database when Alice performed her +GET request for the blog post. If this happens you might see lost +updates you wanted to prevent with Optimistic Locking. + +See the example code, The form (GET Request): + +.. code-block:: php + + find('BlogPost', 123456); + + echo ''; + echo ''; + +And the change headline action (POST Request): + +.. code-block:: php + + find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion); + +Pessimistic Locking +~~~~~~~~~~~~~~~~~~~ + +Doctrine 2 supports Pessimistic Locking at the database level. No +attempt is being made to implement pessimistic locking inside +Doctrine, rather vendor-specific and ANSI-SQL commands are used to +acquire row-level locks. Every Entity can be part of a pessimistic +lock, there is no special metadata required to use this feature. + +However for Pessimistic Locking to work you have to disable the +Auto-Commit Mode of your Database and start a transaction around +your pessimistic lock use-case using the "Approach 2: Explicit +Transaction Demarcation" described above. Doctrine 2 will throw an +Exception if you attempt to acquire an pessimistic lock and no +transaction is running. + +Doctrine 2 currently supports two pessimistic lock modes: + + +- Pessimistic Write + (``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the + underlying database rows for concurrent Read and Write Operations. +- Pessimistic Read (``Doctrine\DBAL\LockMode::PESSIMISTIC_READ``), + locks other concurrent requests that attempt to update or lock rows + in write mode. + +You can use pessimistic locks in three different scenarios: + + +1. Using + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +2. Using + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +3. Using + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst new file mode 100644 index 00000000..da3cc9dc --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst @@ -0,0 +1,60 @@ +Association Updates: Owning Side and Inverse Side +================================================= + +When mapping bidirectional associations it is important to +understand the concept of the owning and inverse sides. The +following general rules apply: + +- Relationships may be bidirectional or unidirectional. +- A bidirectional relationship has both an owning side and an inverse side +- A unidirectional relationship only has an owning side. +- Doctrine will **only** check the owning side of an association for changes. + +Bidirectional Associations +-------------------------- + +The following rules apply to **bidirectional** associations: + +- The inverse side has to use the ``mappedBy`` attribute of the OneToOne, + OneToMany, or ManyToMany mapping declaration. The mappedBy + attribute contains the name of the association-field on the owning side. +- The owning side has to use the ``inversedBy`` attribute of the + OneToOne, ManyToOne, or ManyToMany mapping declaration. + The inversedBy attribute contains the name of the association-field + on the inverse-side. +- ManyToOne is always the owning side of a bidirectional association. +- OneToMany is always the inverse side of a bidirectional association. +- The owning side of a OneToOne association is the entity with the table + containing the foreign key. +- You can pick the owning side of a many-to-many association yourself. + +Important concepts +------------------ + +**Doctrine will only check the owning side of an association for changes.** + +To fully understand this, remember how bidirectional associations +are maintained in the object world. There are 2 references on each +side of the association and these 2 references both represent the +same association but can change independently of one another. Of +course, in a correct application the semantics of the bidirectional +association are properly maintained by the application developer +(that's his responsibility). Doctrine needs to know which of these +two in-memory references is the one that should be persisted and +which not. This is what the owning/inverse concept is mainly used +for. + +**Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine's point of view)** + +The owning side of a bidirectional association is the side Doctrine +"looks at" when determining the state of the association, and +consequently whether there is anything to do to update the +association in the database. + +.. note:: + + "Owning side" and "inverse side" are technical concepts of + the ORM technology, not concepts of your domain model. What you + consider as the owning side in your domain model can be different + from what the owning side is for Doctrine. These are unrelated. + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst new file mode 100644 index 00000000..f01c3f91 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst @@ -0,0 +1,201 @@ +Doctrine Internals explained +============================ + +Object relational mapping is a complex topic and sufficiently understanding how Doctrine works internally helps you use its full power. + +How Doctrine keeps track of Objects +----------------------------------- + +Doctrine uses the Identity Map pattern to track objects. Whenever you fetch an +object from the database, Doctrine will keep a reference to this object inside +its UnitOfWork. The array holding all the entity references is two-levels deep +and has the keys "root entity name" and "id". Since Doctrine allows composite +keys the id is a sorted, serialized version of all the key columns. + +This allows Doctrine room for optimizations. If you call the EntityManager and +ask for an entity with a specific ID twice, it will return the same instance: + +.. code-block:: php + + public function testIdentityMap() + { + $objectA = $this->entityManager->find('EntityName', 1); + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +Only one SELECT query will be fired against the database here. In the second +``EntityManager#find()`` call Doctrine will check the identity map first and +doesn't need to make that database roundtrip. + +Even if you get a proxy object first then fetch the object by the same id you +will still end up with the same reference: + +.. code-block:: php + + public function testIdentityMapReference() + { + $objectA = $this->entityManager->getReference('EntityName', 1); + // check for proxyinterface + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA); + + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +The identity map being indexed by primary keys only allows shortcuts when you +ask for objects by primary key. Assume you have the following ``persons`` +table: + +:: + + id | name + ------------- + 1 | Benjamin + 2 | Bud + +Take the following example where two +consecutive calls are made against a repository to fetch an entity by a set of +criteria: + +.. code-block:: php + + public function testIdentityMapRepositoryFindBy() + { + $repository = $this->entityManager->getRepository('Person'); + $objectA = $repository->findOneBy(array('name' => 'Benjamin')); + $objectB = $repository->findOneBy(array('name' => 'Benjamin')); + + $this->assertSame($objectA, $objectB); + } + +This query will still return the same references and `$objectA` and `$objectB` +are indeed referencing the same object. However when checking your SQL logs you +will realize that two queries have been executed against the database. Doctrine +only knows objects by id, so a query for different criteria has to go to the +database, even if it was executed just before. + +But instead of creating a second Person object Doctrine first gets the primary +key from the row and check if it already has an object inside the UnitOfWork +with that primary key. In our example it finds an object and decides to return +this instead of creating a new one. + +The identity map has a second use-case. When you call ``EntityManager#flush`` +Doctrine will ask the identity map for all objects that are currently managed. +This means you don't have to call ``EntityManager#persist`` over and over again +to pass known objects to the EntityManager. This is a NO-OP for known entities, +but leads to much code written that is confusing to other developers. + +The following code WILL update your database with the changes made to the +``Person`` object, even if you did not call ``EntityManager#persist``: + +.. code-block:: php + + find("Person", 1); + $user->setName("Guilherme"); + $entityManager->flush(); + +How Doctrine Detects Changes +---------------------------- + +Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI). +This means you map php objects into a relational database that don't +necessarily know about the database at all. A natural question would now be, +"how does Doctrine even detect objects have changed?". + +For this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch +an object from the database Doctrine will keep a copy of all the properties and +associations inside the UnitOfWork. Because variables in the PHP language are +subject to "copy-on-write" the memory usage of a PHP request that only reads +objects from the database is the same as if Doctrine did not keep this variable +copy. Only if you start changing variables PHP will create new variables internally +that consume new memory. + +Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the +Identity Map and for each object compares the original property and association +values with the values that are currently set on the object. If changes are +detected then the object is queued for a SQL UPDATE operation. Only the fields +that actually changed are updated. + +This process has an obvious performance impact. The larger the size of the +UnitOfWork is, the longer this computation takes. There are several ways to +optimize the performance of the Flush Operation: + +- Mark entities as read only. These entities can only be inserted or removed, + but are never updated. They are omitted in the changeset calculation. +- Temporarily mark entities as read only. If you have a very large UnitOfWork + but know that a large set of entities has not changed, just mark them as read + only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``. +- Flush only a single entity with ``$entityManager->flush($entity)``. +- Use :doc:`Change Tracking Policies ` to use more + explicit strategies of notifying the UnitOfWork what objects/properties + changed. + + +Query Internals +--------------- + +The different ORM Layers +------------------------ + +Doctrine ships with a set of layers with different responsibilities. This +section gives a short explanation of each layer. + +Hydration +~~~~~~~~~ + +Responsible for creating a final result from a raw database statement and a +result-set mapping object. The developer can choose which kind of result he +wishes to be hydrated. Default result-types include: + +- SQL to Entities +- SQL to structured Arrays +- SQL to simple scalar result arrays +- SQL to a single result variable + +Hydration to entities and arrays is one of most complex parts of Doctrine +algorithm-wise. It can built results with for example: + +- Single table selects +- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent. +- Mixed results of objects and scalar values +- Hydration of results by a given scalar value as key. + +Persisters +~~~~~~~~~~ + +tbr + +UnitOfWork +~~~~~~~~~~ + +tbr + +ResultSetMapping +~~~~~~~~~~~~~~~~ + +tbr + +DQL Parser +~~~~~~~~~~ + +tbr + +SQLWalker +~~~~~~~~~ + +tbr + +EntityManager +~~~~~~~~~~~~~ + +tbr + +ClassMetadataFactory +~~~~~~~~~~~~~~~~~~~~ + +tbr + diff --git a/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst new file mode 100644 index 00000000..0efba84f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst @@ -0,0 +1,712 @@ +Working with Associations +========================= + +Associations between entities are represented just like in regular +object-oriented PHP, with references to other objects or +collections of objects. When it comes to persistence, it is +important to understand three main things: + + +- The :doc:`concept of owning and inverse sides ` + in bidirectional associations. +- If an entity is removed from a collection, the association is + removed, not the entity itself. A collection of entities always + only represents the association to the containing entities, not the + entity itself. +- Collection-valued :ref:`persistent fields ` have to be instances of the + ``Doctrine\Common\Collections\Collection`` interface. + +Changes to associations in your code are not synchronized to the +database directly, but upon calling ``EntityManager#flush()``. + +To describe all the concepts of working with associations we +introduce a specific set of example entities that show all the +different flavors of association management in Doctrine. + +Association Example Entities +---------------------------- + +We will use a simple comment system with Users and Comments as +entities to show examples of association management. See the PHP +docblocks of each association in the following example for +information about its type and if it's the owning or inverse side. + +.. code-block:: php + + commentsRead; + } + + public function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + } + +The interaction code would then look like in the following snippet +(``$em`` here is an instance of the EntityManager): + +.. code-block:: php + + find('User', $userId); + + // unidirectional many to many + $comment = $em->find('Comment', $readCommentId); + $user->getReadComments()->add($comment); + + $em->flush(); + + // unidirectional many to one + $myFirstComment = new Comment(); + $user->setFirstComment($myFirstComment); + + $em->persist($myFirstComment); + $em->flush(); + +In the case of bi-directional associations you have to update the +fields on both sides: + +.. code-block:: php + + commentsAuthored; + } + + public function getFavoriteComments() { + return $this->favorites; + } + } + + class Comment + { + // ... + + public function getUserFavorites() { + return $this->userFavorites; + } + + public function setAuthor(User $author = null) { + $this->author = $author; + } + } + + // Many-to-Many + $user->getFavorites()->add($favoriteComment); + $favoriteComment->getUserFavorites()->add($user); + + $em->flush(); + + // Many-To-One / One-To-Many Bidirectional + $newComment = new Comment(); + $user->getAuthoredComments()->add($newComment); + $newComment->setAuthor($user); + + $em->persist($newComment); + $em->flush(); + +Notice how always both sides of the bidirectional association are +updated. The previous unidirectional associations were simpler to +handle. + +Removing Associations +--------------------- + +Removing an association between two entities is similarly +straight-forward. There are two strategies to do so, by key and by +element. Here are some examples: + +.. code-block:: php + + getComments()->removeElement($comment); + $comment->setAuthor(null); + + $user->getFavorites()->removeElement($comment); + $comment->getUserFavorites()->removeElement($user); + + // Remove by Key + $user->getComments()->remove($ithComment); + $comment->setAuthor(null); + +You need to call ``$em->flush()`` to make persist these changes in +the database permanently. + +Notice how both sides of the bidirectional association are always +updated. Unidirectional associations are consequently simpler to +handle. Also note that if you use type-hinting in your methods, i.e. +``setAddress(Address $address)``, PHP will only allow null +values if ``null`` is set as default value. Otherwise +setAddress(null) will fail for removing the association. If you +insist on type-hinting a typical way to deal with this is to +provide a special method, like ``removeAddress()``. This can also +provide better encapsulation as it hides the internal meaning of +not having an address. + +When working with collections, keep in mind that a Collection is +essentially an ordered map (just like a PHP array). That is why the +``remove`` operation accepts an index/key. ``removeElement`` is a +separate method that has O(n) complexity using ``array_search``, +where n is the size of the map. + +.. note:: + + Since Doctrine always only looks at the owning side of a + bidirectional association for updates, it is not necessary for + write operations that an inverse collection of a bidirectional + one-to-many or many-to-many association is updated. This knowledge + can often be used to improve performance by avoiding the loading of + the inverse collection. + + +You can also clear the contents of a whole collection using the +``Collections::clear()`` method. You should be aware that using +this method can lead to a straight and optimized database delete or +update call during the flush operation that is not aware of +entities that have been re-added to the collection. + +Say you clear a collection of tags by calling +``$post->getTags()->clear();`` and then call +``$post->getTags()->add($tag)``. This will not recognize the tag having +already been added previously and will consequently issue two separate database +calls. + +Association Management Methods +------------------------------ + +It is generally a good idea to encapsulate proper association +management inside the entity classes. This makes it easier to use +the class correctly and can encapsulate details about how the +association is maintained. + +The following code shows updates to the previous User and Comment +example that encapsulate much of the association management code: + +.. code-block:: php + + commentsRead[] = $comment; + } + + public function addComment(Comment $comment) { + if (count($this->commentsAuthored) == 0) { + $this->setFirstComment($comment); + } + $this->comments[] = $comment; + $comment->setAuthor($this); + } + + private function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + + public function addFavorite(Comment $comment) { + $this->favorites->add($comment); + $comment->addUserFavorite($this); + } + + public function removeFavorite(Comment $comment) { + $this->favorites->removeElement($comment); + $comment->removeUserFavorite($this); + } + } + + class Comment + { + // .. + + public function addUserFavorite(User $user) { + $this->userFavorites[] = $user; + } + + public function removeUserFavorite(User $user) { + $this->userFavorites->removeElement($user); + } + } + +You will notice that ``addUserFavorite`` and ``removeUserFavorite`` +do not call ``addFavorite`` and ``removeFavorite``, thus the +bidirectional association is strictly-speaking still incomplete. +However if you would naively add the ``addFavorite`` in +``addUserFavorite``, you end up with an infinite loop, so more work +is needed. As you can see, proper bidirectional association +management in plain OOP is a non-trivial task and encapsulating all +the details inside the classes can be challenging. + +.. note:: + + If you want to make sure that your collections are perfectly + encapsulated you should not return them from a + ``getCollectionName()`` method directly, but call + ``$collection->toArray()``. This way a client programmer for the + entity cannot circumvent the logic you implement on your entity for + association management. For example: + + +.. code-block:: php + + commentsRead->toArray(); + } + } + +This will however always initialize the collection, with all the +performance penalties given the size. In some scenarios of large +collections it might even be a good idea to completely hide the +read access behind methods on the EntityRepository. + +There is no single, best way for association management. It greatly +depends on the requirements of your concrete domain model as well +as your preferences. + +Synchronizing Bidirectional Collections +--------------------------------------- + +In the case of Many-To-Many associations you as the developer have the +responsibility of keeping the collections on the owning and inverse side +in sync when you apply changes to them. Doctrine can only +guarantee a consistent state for the hydration, not for your client +code. + +Using the User-Comment entities from above, a very simple example +can show the possible caveats you can encounter: + +.. code-block:: php + + getFavorites()->add($favoriteComment); + // not calling $favoriteComment->getUserFavorites()->add($user); + + $user->getFavorites()->contains($favoriteComment); // TRUE + $favoriteComment->getUserFavorites()->contains($user); // FALSE + +There are two approaches to handle this problem in your code: + + +1. Ignore updating the inverse side of bidirectional collections, + BUT never read from them in requests that changed their state. In + the next Request Doctrine hydrates the consistent collection state + again. +2. Always keep the bidirectional collections in sync through + association management methods. Reads of the Collections directly + after changes are consistent then. + +Transitive persistence / Cascade Operations +------------------------------------------- + +Persisting, removing, detaching and merging individual entities can +become pretty cumbersome, especially when a highly interweaved object graph +is involved. Therefore Doctrine 2 provides a +mechanism for transitive persistence through cascading of these +operations. Each association to another entity or a collection of +entities can be configured to automatically cascade certain +operations. By default, no operations are cascaded. + +The following cascade options exist: + + +- persist : Cascades persist operations to the associated + entities. +- remove : Cascades remove operations to the associated entities. +- merge : Cascades merge operations to the associated entities. +- detach : Cascades detach operations to the associated entities. +- all : Cascades persist, remove, merge and detach operations to + associated entities. + +.. note:: + + Cascade operations are performed in memory. That means collections and related entities + are fetched into memory, even if they are still marked as lazy when + the cascade operation is about to be performed. However this approach allows + entity lifecycle events to be performed for each of these operations. + + However, pulling objects graph into memory on cascade can cause considerable performance + overhead, especially when cascading collections are large. Makes sure + to weigh the benefits and downsides of each cascade operation that you define. + + To rely on the database level cascade operations for the delete operation instead, you can + configure each join column with the **onDelete** option. See the respective + mapping driver chapters for more information. + +The following example is an extension to the User-Comment example +of this chapter. Suppose in our application a user is created +whenever he writes his first comment. In this case we would use the +following code: + +.. code-block:: php + + addComment($myFirstComment); + + $em->persist($user); + $em->persist($myFirstComment); + $em->flush(); + +Even if you *persist* a new User that contains our new Comment this +code would fail if you removed the call to +``EntityManager#persist($myFirstComment)``. Doctrine 2 does not +cascade the persist operation to all nested entities that are new +as well. + +More complicated is the deletion of all of a user's comments when he is +removed from the system: + +.. code-block:: php + + $user = $em->find('User', $deleteUserId); + + foreach ($user->getAuthoredComments() AS $comment) { + $em->remove($comment); + } + $em->remove($user); + $em->flush(); + +Without the loop over all the authored comments Doctrine would use +an UPDATE statement only to set the foreign key to NULL and only +the User would be deleted from the database during the +flush()-Operation. + +To have Doctrine handle both cases automatically we can change the +``User#commentsAuthored`` property to cascade both the "persist" +and the "remove" operation. + +.. code-block:: php + + addresses = new ArrayCollection(); + } + + public function newStandingData(StandingData $sd) + { + $this->standingData = $sd; + } + + public function removeAddress($pos) + { + unset($this->addresses[$pos]); + } + } + +Now two examples of what happens when you remove the references: + +.. code-block:: php + + find("Addressbook\Contact", $contactId); + $contact->newStandingData(new StandingData("Firstname", "Lastname", "Street")); + $contact->removeAddress(1); + + $em->flush(); + +In this case you have not only changed the ``Contact`` entity itself but +you have also removed the references for standing data and as well as one +address reference. When flush is called not only are the references removed +but both the old standing data and the one address entity are also deleted +from the database. + +Filtering Collections +--------------------- + +.. filtering-collections: + +Collections have a filtering API that allows to slice parts of data from +a collection. If the collection has not been loaded from the database yet, +the filtering API can work on the SQL level to make optimized access to +large collections. + +.. code-block:: php + + find('Group', $groupId); + $userCollection = $group->getUsers(); + + $criteria = Criteria::create() + ->where(Criteria::expr()->eq("birthday", "1982-02-17")) + ->orderBy(array("username" => "ASC")) + ->setFirstResult(0) + ->setMaxResults(20) + ; + + $birthdayUsers = $userCollection->matching($criteria); + +.. tip:: + + You can move the access of slices of collections into dedicated methods of + an entity. For example ``Group#getTodaysBirthdayUsers()``. + +The Criteria has a limited matching language that works both on the +SQL and on the PHP collection level. This means you can use collection matching +interchangeably, independent of in-memory or sql-backed collections. + +.. code-block:: php + + find('CMS\Article', 1234); + $article->setHeadline('Hello World dude!'); + + $article2 = $entityManager->find('CMS\Article', 1234); + echo $article2->getHeadline(); + +In this case the Article is accessed from the entity manager twice, +but modified in between. Doctrine 2 realizes this and will only +ever give you access to one instance of the Article with ID 1234, +no matter how often do you retrieve it from the EntityManager and +even no matter what kind of Query method you are using (find, +Repository Finder or DQL). This is called "Identity Map" pattern, +which means Doctrine keeps a map of each entity and ids that have +been retrieved per PHP request and keeps returning you the same +instances. + +In the previous example the echo prints "Hello World dude!" to the +screen. You can even verify that ``$article`` and ``$article2`` are +indeed pointing to the same instance by running the following +code: + +.. code-block:: php + + comments = new ArrayCollection(); + } + + public function getAuthor() { return $this->author; } + public function getComments() { return $this->comments; } + } + + $article = $em->find('Article', 1); + +This code only retrieves the ``Article`` instance with id 1 executing +a single SELECT statement against the user table in the database. +You can still access the associated properties author and comments +and the associated objects they contain. + +This works by utilizing the lazy loading pattern. Instead of +passing you back a real Author instance and a collection of +comments Doctrine will create proxy instances for you. Only if you +access these proxies for the first time they will go through the +EntityManager and load their state from the database. + +This lazy-loading process happens behind the scenes, hidden from +your code. See the following code: + +.. code-block:: php + + find('Article', 1); + + // accessing a method of the user instance triggers the lazy-load + echo "Author: " . $article->getAuthor()->getName() . "\n"; + + // Lazy Loading Proxies pass instanceof tests: + if ($article->getAuthor() instanceof User) { + // a User Proxy is a generated "UserProxy" class + } + + // accessing the comments as an iterator triggers the lazy-load + // retrieving ALL the comments of this article from the database + // using a single SELECT statement + foreach ($article->getComments() AS $comment) { + echo $comment->getText() . "\n\n"; + } + + // Article::$comments passes instanceof tests for the Collection interface + // But it will NOT pass for the ArrayCollection interface + if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) { + echo "This will always be true!"; + } + +A slice of the generated proxy classes code looks like the +following piece of code. A real proxy class override ALL public +methods along the lines of the ``getName()`` method shown below: + +.. code-block:: php + + _load(); + return parent::getName(); + } + // .. other public methods of User + } + +.. warning:: + + Traversing the object graph for parts that are lazy-loaded will + easily trigger lots of SQL queries and will perform badly if used + to heavily. Make sure to use DQL to fetch-join all the parts of the + object-graph that you need as efficiently as possible. + + +Persisting entities +------------------- + +An entity can be made persistent by passing it to the +``EntityManager#persist($entity)`` method. By applying the persist +operation on some entity, that entity becomes MANAGED, which means +that its persistence is from now on managed by an EntityManager. As +a result the persistent state of such an entity will subsequently +be properly synchronized with the database when +``EntityManager#flush()`` is invoked. + +.. note:: + + Invoking the ``persist`` method on an entity does NOT + cause an immediate SQL INSERT to be issued on the database. + Doctrine applies a strategy called "transactional write-behind", + which means that it will delay most SQL commands until + ``EntityManager#flush()`` is invoked which will then issue all + necessary SQL statements to synchronize your objects with the + database in the most efficient way and a single, short transaction, + taking care of maintaining referential integrity. + + +Example: + +.. code-block:: php + + setName('Mr.Right'); + $em->persist($user); + $em->flush(); + +.. note:: + + Generated entity identifiers / primary keys are + guaranteed to be available after the next successful flush + operation that involves the entity in question. You can not rely on + a generated identifier to be available directly after invoking + ``persist``. The inverse is also true. You can not rely on a + generated identifier being not available after a failed flush + operation. + + +The semantics of the persist operation, applied on an entity X, are +as follows: + + +- If X is a new entity, it becomes managed. The entity X will be + entered into the database as a result of the flush operation. +- If X is a preexisting managed entity, it is ignored by the + persist operation. However, the persist operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities are mapped with cascade=PERSIST or cascade=ALL (see + "Transitive Persistence"). +- If X is a removed entity, it becomes managed. +- If X is a detached entity, an exception will be thrown on + flush. + +Removing entities +----------------- + +An entity can be removed from persistent storage by passing it to +the ``EntityManager#remove($entity)`` method. By applying the +``remove`` operation on some entity, that entity becomes REMOVED, +which means that its persistent state will be deleted once +``EntityManager#flush()`` is invoked. + +.. note:: + + Just like ``persist``, invoking ``remove`` on an entity + does NOT cause an immediate SQL DELETE to be issued on the + database. The entity will be deleted on the next invocation of + ``EntityManager#flush()`` that involves that entity. This + means that entities scheduled for removal can still be queried + for and appear in query and collection results. See + the section on :ref:`Database and UnitOfWork Out-Of-Sync ` + for more information. + + +Example: + +.. code-block:: php + + remove($user); + $em->flush(); + +The semantics of the remove operation, applied to an entity X are +as follows: + + +- If X is a new entity, it is ignored by the remove operation. + However, the remove operation is cascaded to entities referenced by + X, if the relationship from X to these other entities is mapped + with cascade=REMOVE or cascade=ALL (see "Transitive Persistence"). +- If X is a managed entity, the remove operation causes it to + become removed. The remove operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=REMOVE or cascade=ALL (see + "Transitive Persistence"). +- If X is a detached entity, an InvalidArgumentException will be + thrown. +- If X is a removed entity, it is ignored by the remove operation. +- A removed entity X will be removed from the database as a result + of the flush operation. + +After an entity has been removed its in-memory state is the same as +before the removal, except for generated identifiers. + +Removing an entity will also automatically delete any existing +records in many-to-many join tables that link this entity. The +action taken depends on the value of the ``@joinColumn`` mapping +attribute "onDelete". Either Doctrine issues a dedicated ``DELETE`` +statement for records of each join table or it depends on the +foreign key semantics of onDelete="CASCADE". + +Deleting an object with all its associated objects can be achieved +in multiple ways with very different performance impacts. + + +1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2 + will fetch this association. If its a Single association it will + pass this entity to + ´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()\`. + In both cases the cascade remove semantics are applied recursively. + For large object graphs this removal strategy can be very costly. +2. Using a DQL ``DELETE`` statement allows you to delete multiple + entities of a type with a single command and without hydrating + these entities. This can be very efficient to delete large object + graphs from the database. +3. Using foreign key semantics ``onDelete="CASCADE"`` can force the + database to remove all associated objects internally. This strategy + is a bit tricky to get right but can be very powerful and fast. You + should be aware however that using strategy 1 (``CASCADE=REMOVE``) + completely by-passes any foreign key ``onDelete=CASCADE`` option, + because Doctrine will fetch and remove all associated entities + explicitly nevertheless. + +Detaching entities +------------------ + +An entity is detached from an EntityManager and thus no longer +managed by invoking the ``EntityManager#detach($entity)`` method on +it or by cascading the detach operation to it. Changes made to the +detached entity, if any (including removal of the entity), will not +be synchronized to the database after the entity has been +detached. + +Doctrine will not hold on to any references to a detached entity. + +Example: + +.. code-block:: php + + detach($entity); + +The semantics of the detach operation, applied to an entity X are +as follows: + + +- If X is a managed entity, the detach operation causes it to + become detached. The detach operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. +- If X is a new or detached entity, it is ignored by the detach + operation. +- If X is a removed entity, the detach operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. + +There are several situations in which an entity is detached +automatically without invoking the ``detach`` method: + + +- When ``EntityManager#clear()`` is invoked, all entities that are + currently managed by the EntityManager instance become detached. +- When serializing an entity. The entity retrieved upon subsequent + unserialization will be detached (This is the case for all entities + that are serialized and stored in some cache, i.e. when using the + Query Result Cache). + +The ``detach`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. + +Merging entities +---------------- + +Merging entities refers to the merging of (usually detached) +entities into the context of an EntityManager so that they become +managed again. To merge the state of an entity into an +EntityManager use the ``EntityManager#merge($entity)`` method. The +state of the passed entity will be merged into a managed copy of +this entity and this copy will subsequently be returned. + +Example: + +.. code-block:: php + + merge($detachedEntity); + // $entity now refers to the fully managed copy returned by the merge operation. + // The EntityManager $em now manages the persistence of $entity as usual. + +.. note:: + + When you want to serialize/unserialize entities you + have to make all entity properties protected, never private. The + reason for this is, if you serialize a class that was a proxy + instance before, the private variables won't be serialized and a + PHP Notice is thrown. + + +The semantics of the merge operation, applied to an entity X, are +as follows: + + +- If X is a detached entity, the state of X is copied onto a + pre-existing managed entity instance X' of the same identity. +- If X is a new entity instance, a new managed copy X' will be + created and the state of X is copied onto this managed instance. +- If X is a removed entity instance, an InvalidArgumentException + will be thrown. +- If X is a managed entity, it is ignored by the merge operation, + however, the merge operation is cascaded to entities referenced by + relationships from X if these relationships have been mapped with + the cascade element value MERGE or ALL (see "Transitive + Persistence"). +- For all entities Y referenced by relationships from X having the + cascade element value MERGE or ALL, Y is merged recursively as Y'. + For all such Y referenced by X, X' is set to reference Y'. (Note + that if X is managed then X is the same object as X'.) +- If X is an entity merged to X', with a reference to another + entity Y, where cascade=MERGE or cascade=ALL is not specified, then + navigation of the same association from X' yields a reference to a + managed object Y' with the same persistent identity as Y. + +The ``merge`` operation will throw an ``OptimisticLockException`` +if the entity being merged uses optimistic locking through a +version field and the versions of the entity being merged and the +managed copy don't match. This usually means that the entity has +been modified while being detached. + +The ``merge`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. The most common scenario for +the ``merge`` operation is to reattach entities to an EntityManager +that come from some cache (and are therefore detached) and you want +to modify and persist such an entity. + +.. warning:: + + If you need to perform multiple merges of entities that share certain subparts + of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the + successive calls to ``EntityManager#merge()``. Otherwise you might end up with + multiple copies of the "same" object in the database, however with different ids. + +.. note:: + + If you load some detached entities from a cache and you do + not need to persist or delete them or otherwise make use of them + without the need for persistence services there is no need to use + ``merge``. I.e. you can simply pass detached objects from a cache + directly to the view. + + +Synchronization with the Database +--------------------------------- + +The state of persistent entities is synchronized with the database +on flush of an ``EntityManager`` which commits the underlying +``UnitOfWork``. The synchronization involves writing any updates to +persistent entities and their relationships to the database. +Thereby bidirectional relationships are persisted based on the +references held by the owning side of the relationship as explained +in the Association Mapping chapter. + +When ``EntityManager#flush()`` is called, Doctrine inspects all +managed, new and removed entities and will perform the following +operations. + +.. _workingobjects_database_uow_outofsync: + +Effects of Database and UnitOfWork being Out-Of-Sync +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As soon as you begin to change the state of entities, call persist or remove the +contents of the UnitOfWork and the database will drive out of sync. They can +only be synchronized by calling ``EntityManager#flush()``. This section +describes the effects of database and UnitOfWork being out of sync. + +- Entities that are scheduled for removal can still be queried from the database. + They are returned from DQL and Repository queries and are visible in collections. +- Entities that are passed to ``EntityManager#persist`` do not turn up in query + results. +- Entities that have changed will not be overwritten with the state from the database. + This is because the identity map will detect the construction of an already existing + entity and assumes its the most up to date version. + +``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually. + +Synchronizing New and Managed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a managed entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + UPDATE statement, only if at least one persistent field has + changed. +- No SQL updates are executed if the entity did not change. + +The flush operation applies to a new entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + INSERT statement. + +For all (initialized) relationships of the new or managed entity +the following semantics apply to each associated entity X: + + +- If X is new and persist operations are configured to cascade on + the relationship, X will be persisted. +- If X is new and no persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error. +- If X is removed and persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error (X would be re-persisted by the cascade). +- If X is detached and persist operations are configured to + cascade on the relationship, an exception will be thrown (This is + semantically the same as passing X to persist()). + +Synchronizing Removed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a removed entity by deleting its +persistent state from the database. No cascade options are relevant +for removed entities on flush, the cascade remove option is already +executed during ``EntityManager#remove($entity)``. + +The size of a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The size of a Unit of Work mainly refers to the number of managed +entities at a particular point in time. + +The cost of flushing +~~~~~~~~~~~~~~~~~~~~ + +How costly a flush operation is, mainly depends on two factors: + + +- The size of the EntityManager's current UnitOfWork. +- The configured change tracking policies + +You can get the size of a UnitOfWork as follows: + +.. code-block:: php + + getUnitOfWork()->size(); + +The size represents the number of managed entities in the Unit of +Work. This size affects the performance of flush() operations due +to change tracking (see "Change Tracking Policies") and, of course, +memory consumption, so you may want to check it from time to time +during development. + +.. note:: + + Do not invoke ``flush`` after every change to an entity + or every single invocation of persist/remove/merge/... This is an + anti-pattern and unnecessarily reduces the performance of your + application. Instead, form units of work that operate on your + objects and call ``flush`` when you are done. While serving a + single HTTP request there should be usually no need for invoking + ``flush`` more than 0-2 times. + + +Direct access to a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get direct access to the Unit of Work by calling +``EntityManager#getUnitOfWork()``. This will return the UnitOfWork +instance the EntityManager is currently using. + +.. code-block:: php + + getUnitOfWork(); + +.. note:: + + Directly manipulating a UnitOfWork is not recommended. + When working directly with the UnitOfWork API, respect methods + marked as INTERNAL by not using them and carefully read the API + documentation. + + +Entity State +~~~~~~~~~~~~ + +As outlined in the architecture overview an entity can be in one of +four possible states: NEW, MANAGED, REMOVED, DETACHED. If you +explicitly need to find out what the current state of an entity is +in the context of a certain ``EntityManager`` you can ask the +underlying ``UnitOfWork``: + +.. code-block:: php + + getUnitOfWork()->getEntityState($entity)) { + case UnitOfWork::STATE_MANAGED: + ... + case UnitOfWork::STATE_REMOVED: + ... + case UnitOfWork::STATE_DETACHED: + ... + case UnitOfWork::STATE_NEW: + ... + } + +An entity is in MANAGED state if it is associated with an +``EntityManager`` and it is not REMOVED. + +An entity is in REMOVED state after it has been passed to +``EntityManager#remove()`` until the next flush operation of the +same EntityManager. A REMOVED entity is still associated with an +``EntityManager`` until the next flush operation. + +An entity is in DETACHED state if it has persistent state and +identity but is currently not associated with an +``EntityManager``. + +An entity is in NEW state if has no persistent state and identity +and is not associated with an ``EntityManager`` (for example those +just created via the "new" operator). + +Querying +-------- + +Doctrine 2 provides the following ways, in increasing level of +power and flexibility, to query for persistent objects. You should +always start with the simplest one that suits your needs. + +By Primary Key +~~~~~~~~~~~~~~ + +The most basic way to query for a persistent object is by its +identifier / primary key using the +``EntityManager#find($entityName, $id)`` method. Here is an +example: + +.. code-block:: php + + find('MyProject\Domain\User', $id); + +The return value is either the found entity instance or null if no +instance could be found with the given identifier. + +Essentially, ``EntityManager#find()`` is just a shortcut for the +following: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->find($id); + +``EntityManager#getRepository($entityName)`` returns a repository +object which provides many ways to retrieve entities of the +specified type. By default, the repository instance is of type +``Doctrine\ORM\EntityRepository``. You can also use custom +repository classes as shown later. + +By Simple Conditions +~~~~~~~~~~~~~~~~~~~~ + +To query for one or more entities based on several conditions that +form a logical conjunction, use the ``findBy`` and ``findOneBy`` +methods on a repository as follows: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20)); + + // All users that are 20 years old and have a surname of 'Miller' + $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller')); + + // A single user by its nickname + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + +You can also load by owning side associations through the repository: + +.. code-block:: php + + find('MyProject\Domain\Phonenumber', 1234); + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId())); + +Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself. + +The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0); + +If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40))); + // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40) + +An EntityRepository also provides a mechanism for more concise +calls through its use of ``__call``. Thus, the following two +examples are equivalent: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + + // A single user by its nickname (__call magic) + $user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb'); + +By Criteria +~~~~~~~~~~~ + +.. versionadded:: 2.3 + +The Repository implement the ``Doctrine\Common\Collections\Selectable`` +interface. That means you can build ``Doctrine\Common\Collections\Criteria`` +and pass them to the ``matching($criteria)`` method. + +See the :ref:`Working with Associations: Filtering collections +`. + +By Eager Loading +~~~~~~~~~~~~~~~~ + +Whenever you query for an entity that has persistent associations +and these associations are mapped as EAGER, they will automatically +be loaded together with the entity being queried and is thus +immediately available to your application. + +By Lazy Loading +~~~~~~~~~~~~~~~ + +Whenever you have a managed entity instance at hand, you can +traverse and use any associations of that entity that are +configured LAZY as if they were in-memory already. Doctrine will +automatically load the associated objects on demand through the +concept of lazy-loading. + +By DQL +~~~~~~ + +The most powerful and flexible method to query for persistent +objects is the Doctrine Query Language, an object query language. +DQL enables you to query for persistent objects in the language of +objects. DQL understands classes, fields, inheritance and +associations. DQL is syntactically very similar to the familiar SQL +but *it is not SQL*. + +A DQL query is represented by an instance of the +``Doctrine\ORM\Query`` class. You create a query using +``EntityManager#createQuery($dql)``. Here is a simple example: + +.. code-block:: php + + createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30"); + $users = $q->getResult(); + +Note that this query contains no knowledge about the relational +schema, only about the object model. DQL supports positional as +well as named parameters, many functions, (fetch) joins, +aggregates, subqueries and much more. Detailed information about +DQL and its syntax as well as the Doctrine class can be found in +:doc:`the dedicated chapter `. +For programmatically building up queries based on conditions that +are only known at runtime, Doctrine provides the special +``Doctrine\ORM\QueryBuilder`` class. More information on +constructing queries with a QueryBuilder can be found +:doc:`in Query Builder chapter `. + +By Native Queries +~~~~~~~~~~~~~~~~~ + +As an alternative to DQL or as a fallback for special SQL +statements native queries can be used. Native queries are built by +using a hand-crafted SQL query and a ResultSetMapping that +describes how the SQL result set should be transformed by Doctrine. +More information about native queries can be found in +:doc:`the dedicated chapter `. + +Custom Repositories +~~~~~~~~~~~~~~~~~~~ + +By default the EntityManager returns a default implementation of +``Doctrine\ORM\EntityRepository`` when you call +``EntityManager#getRepository($entityClass)``. You can overwrite +this behaviour by specifying the class name of your own Entity +Repository in the Annotation, XML or YAML metadata. In large +applications that require lots of specialized DQL queries using a +custom repository is one recommended way of grouping these queries +in a central location. + +.. code-block:: php + + _em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"') + ->getResult(); + } + } + +You can access your repository now by calling: + +.. code-block:: php + + getRepository('MyDomain\Model\User')->getAllAdminUsers(); + + diff --git a/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst new file mode 100644 index 00000000..d82c82fa --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst @@ -0,0 +1,747 @@ +XML Mapping +=========== + +The XML mapping driver enables you to provide the ORM metadata in +form of XML documents. + +The XML driver is backed by an XML Schema document that describes +the structure of a mapping document. The most recent version of the +XML Schema document is available online at +`http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd `_. +In order to point to the latest version of the document of a +particular stable release branch, just append the release number, +i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with +XML mapping files is to use an IDE/editor that can provide +code-completion based on such an XML Schema document. The following +is an outline of a XML mapping document with the proper xmlns/xsi +setup for the latest code in trunk. + +.. code-block:: xml + + + + ... + + + +The XML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated XML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). For example an Entity with the fully + qualified class-name "MyProject" would require a mapping file + "MyProject.Entities.User.dcm.xml" unless the extension is changed. +- All mapping documents should get the extension ".dcm.xml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +- + +.. code-block:: php + + setFileExtension('.xml'); + +It is recommended to put all XML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the XmlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +Simplified XML Driver +~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the XML Driver. +The changes between the original driver are: + +1. File Extension is .orm.xml +2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.xml +3. You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + '/path/to/files1', + 'OtherProject\Entities' => '/path/to/files2' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.xml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: xml + + // Doctrine.Tests.ORM.Mapping.User.dcm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Be aware that class-names specified in the XML files should be +fully qualified. + +XML-Element Reference +--------------------- + +The XML-Element reference explains all the tags and attributes that +the Doctrine Mapping XSD Schema defines. You should read the +Basic-, Association- and Inheritance Mapping chapters to understand +what each of this definitions means in detail. + +Defining an Entity +~~~~~~~~~~~~~~~~~~ + +Each XML Mapping File contains the definition of one entity, +specified as the ```` element as a direct child of the +```` element: + +.. code-block:: xml + + + + + + + +Required attributes: + + +- name - The fully qualified class-name of the entity. + +Optional attributes: + + +- **table** - The Table-Name to be used for this entity. Otherwise the + Unqualified Class-Name is used by default. +- **repository-class** - The fully qualified class-name of an + alternative ``Doctrine\ORM\EntityRepository`` implementation to be + used with this entity. +- **inheritance-type** - The type of inheritance, defaults to none. A + more detailed description follows in the + *Defining Inheritance Mappings* section. +- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. + +Defining Fields +~~~~~~~~~~~~~~~ + +Each entity class can contain zero to infinite fields that are +managed by Doctrine. You can define them using the ```` +element as a children to the ```` element. The field +element is only used for primitive types that are not the ID of the +entity. For the ID mapping you have to use the ```` element. + +.. code-block:: xml + + + + + + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. + +Optional attributes: + + +- type - The ``Doctrine\DBAL\Types\Type`` name, defaults to + "string" +- column - Name of the column in the database, defaults to the + field name. +- length - The length of the given type, for use with strings + only. +- unique - Should this field contain a unique value across the + table? Defaults to false. +- nullable - Should this field allow NULL as a value? Defaults to + false. +- version - Should this field be used for optimistic locking? Only + works on fields with type integer or datetime. +- scale - Scale of a decimal type. +- precision - Precision of a decimal type. +- column-definition - Optional alternative SQL representation for + this column. This definition begin after the field-name and has to + specify the complete column definition. Using this feature will + turn this field dirty for Schema-Tool update commands at all + times. + +Defining Identity and Generator Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An entity has to have at least one ```` element. For +composite keys you can specify more than one id-element, however +surrogate keys are recommended for use with Doctrine 2. The Id +field allows to define properties of the identifier and allows a +subset of the ```` element attributes: + +.. code-block:: xml + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. +- type - The ``Doctrine\DBAL\Types\Type`` name, preferably + "string" or "integer". + +Optional attributes: + + +- column - Name of the column in the database, defaults to the + field name. + +Using the simplified definition above Doctrine will use no +identifier strategy for this entity. That means you have to +manually set the identifier before calling +``EntityManager#persist($entity)``. This is the so called +``ASSIGNED`` strategy. + +If you want to switch the identifier generation strategy you have +to nest a ```` element inside the id-element. This of +course only works for surrogate keys. For composite keys you always +have to use the ``ASSIGNED`` strategy. + +.. code-block:: xml + + + + + + + +The following values are allowed for the ```` strategy +attribute: + + +- AUTO - Automatic detection of the identifier strategy based on + the preferred solution of the database vendor. +- IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs + available to Doctrine AFTER the INSERT statement has been executed. +- SEQUENCE - Use of a database sequence to retrieve the + entity-ids. This is possible before the INSERT statement is + executed. + +If you are using the SEQUENCE strategy you can define an additional +element to describe the sequence: + +.. code-block:: xml + + + + + + + + +Required attributes for ````: + + +- sequence-name - The name of the sequence + +Optional attributes for ````: + + +- allocation-size - By how much steps should the sequence be + incremented when a value is retrieved. Defaults to 1 +- initial-value - What should the initial value of the sequence + be. + + **NOTE** + + If you want to implement a cross-vendor compatible application you + have to specify and additionally define the + element, if Doctrine chooses the sequence strategy for a + platform. + + +Defining a Mapped Superclass +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to define a class that multiple entities inherit +from, which itself is not an entity however. The chapter on +*Inheritance Mapping* describes a Mapped Superclass in detail. You +can define it in XML using the ```` tag. + +.. code-block:: xml + + + + + + + + +Required attributes: + + +- name - Class name of the mapped superclass. + +You can nest any number of ```` and unidirectional +```` or ```` associations inside a +mapped superclass. + +Defining Inheritance Mappings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are currently two inheritance persistence strategies that you +can choose from when defining entities that inherit from each +other. Single Table inheritance saves the fields of the complete +inheritance hierarchy in a single table, joined table inheritance +creates a table for each entity combining the fields using join +conditions. + +You can specify the inheritance type in the ```` element +and then use the ```` and +```` attributes. + +.. code-block:: xml + + + + + + + + + + +The allowed values for inheritance-type attribute are ``JOINED`` or +``SINGLE_TABLE``. + +.. note:: + + All inheritance related definitions have to be defined on the root + entity of the hierarchy. + + +Defining Lifecycle Callbacks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define the lifecycle callback methods on your entities +using the ```` element: + +.. code-block:: xml + + + + + + + + +Defining One-To-One Relations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define One-To-One Relations/Associations using the +```` element. The required and optional attributes +depend on the associations being on the inverse or owning side. + +For the inverse side the mapping is as simple as: + +.. code-block:: xml + + + + + +Required attributes for inverse One-To-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here Address + entity) that contains the owning side association. + +For the owning side this mapping would look like: + +.. code-block:: xml + + + + + +Required attributes for owning One-to-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes for owning One-to-One: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true, the inverse side entity is always + deleted when the owning side entity is. Defaults to false. +- fetch - Either LAZY or EAGER, defaults to LAZY. This attribute + makes only sense on the owning side, the inverse side *ALWAYS* has + to use the ``FETCH`` strategy. + +The definition for the owning side relies on a bunch of mapping +defaults for the join column names. Without the nested +```` element Doctrine assumes to foreign key to be +called ``user_id`` on the Address Entities table. This is because +the ``MyProject\Address`` entity is the owning side of this +association, which means it contains the foreign key. + +The completed explicitly defined mapping is: + +.. code-block:: xml + + + + + + + +Defining Many-To-One Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-one association is *ALWAYS* the owning side of any +bidirectional association. This simplifies the mapping compared to +the one-to-one case. The minimal mapping for this association looks +like: + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true the entity on the inverse side is + always deleted when the owning side entity is and it is not + connected to any other owning side entity anymore. Defaults to + false. +- fetch - Either LAZY or EAGER, defaults to LAZY. + +This definition relies on a bunch of mapping defaults with regards +to the naming of the join-column/foreign key. The explicitly +defined mapping includes a ```` tag nested inside +the many-to-one association tag: + +.. code-block:: xml + + + + + + + +The join-column attribute ``name`` specifies the column name of the +foreign key and the ``referenced-column-name`` attribute specifies +the name of the primary key column on the User entity. + +Defining One-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The one-to-many association is *ALWAYS* the inverse side of any +association. There exists no such thing as a uni-directional +one-to-many association, which means this association only ever +exists for bi-directional associations. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here + Phonenumber entity) that contains the owning side association. + +Optional attributes: + + +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +Defining Many-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +From all the associations the many-to-many has the most complex +definition. When you rely on the mapping defaults you can omit many +definitions and rely on their implicit values. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- mapped-by - Name of the field on the owning side that contains + the owning side association if the defined many-to-many association + is on the inverse side. +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +The mapping defaults would lead to a join-table with the name +"User\_Group" being created that contains two columns "user\_id" +and "group\_id". The explicit definition of this mapping would be: + +.. code-block:: xml + + + + + + + + + + + + + + +Here both the ```` and ```` +tags are necessary to tell Doctrine for which side the specified +join-columns apply. These are nested inside a ```` +attribute which allows to specify the table name of the +many-to-many join-table. + +Cascade Element +~~~~~~~~~~~~~~~ + +Doctrine allows cascading of several UnitOfWork operations to +related entities. You can specify the cascade operations in the +```` element inside any of the association mapping +tags. + +.. code-block:: xml + + + + + + + + + +Besides ```` the following operations can be +specified by their respective tags: + + +- ```` +- ```` +- ```` +- ```` + +Join Column Element +~~~~~~~~~~~~~~~~~~~ + +In any explicitly defined association mapping you will need the +```` tag. It defines how the foreign key and primary +key names are called that are used for joining two entities. + +Required attributes: + + +- name - The column name of the foreign key. +- referenced-column-name - The column name of the associated + entities primary key + +Optional attributes: + + +- unique - If the join column should contain a UNIQUE constraint. + This makes sense for Many-To-Many join-columns only to simulate a + one-to-many unidirectional using a join-table. +- nullable - should the join column be nullable, defaults to true. +- on-delete - Foreign Key Cascade action to perform when entity is + deleted, defaults to NO ACTION/RESTRICT but can be set to + "CASCADE". + +Defining Order of To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can require one-to-many or many-to-many associations to be +retrieved using an additional ``ORDER BY``. + +.. code-block:: xml + + + + + + + + + +Defining Indexes or Unique Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To define additional indexes or unique constraints on the entities +table you can use the ```` and +```` elements: + +.. code-block:: xml + + + + + + + + + + + + + +You have to specify the column and not the entity-class field names +in the index and unique-constraint definitions. + +Derived Entities ID syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the primary key of an entity contains a foreign key to another entity we speak of a derived +entity relationship. You can define this in XML with the "association-key" attribute in the ```` tag. + +.. code-block:: xml + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst new file mode 100644 index 00000000..9a00d08b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst @@ -0,0 +1,119 @@ +YAML Mapping +============ + +The YAML mapping driver enables you to provide the ORM metadata in +form of YAML documents. + +The YAML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated YAML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). +- All mapping documents should get the extension ".dcm.yml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +- + +.. code-block:: php + + setFileExtension('.yml'); + +It is recommended to put all YAML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the YamlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +Simplified YAML Driver +~~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the YAML Driver. +The changes between the original driver are: + +1. File Extension is .orm.yml +2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.yml +3. You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + 'MyProject\Entities', + '/path/to/files2' => 'OtherProject\Entities' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.yml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: yaml + + # Doctrine.Tests.ORM.Mapping.User.dcm.yml + Doctrine\Tests\ORM\Mapping\User: + type: entity + table: cms_users + indexes: + name_index: + columns: [ name ] + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 50 + oneToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + oneToMany: + phonenumbers: + targetEntity: Phonenumber + mappedBy: user + cascade: ["persist", "merge"] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: cms_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +Be aware that class-names specified in the YAML files should be +fully qualified. + + diff --git a/vendor/doctrine/orm/docs/en/toc.rst b/vendor/doctrine/orm/docs/en/toc.rst new file mode 100644 index 00000000..73f1f25b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/toc.rst @@ -0,0 +1,80 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +Tutorials +--------- + +.. toctree:: + :maxdepth: 1 + + tutorials/getting-started + tutorials/working-with-indexed-associations + tutorials/extra-lazy-associations + tutorials/composite-primary-keys + tutorials/ordered-associations + tutorials/in-ten-quick-steps + tutorials/override-field-association-mappings-in-subclasses + +Reference Guide +--------------- + +.. toctree:: + :maxdepth: 1 + :numbered: + + reference/introduction + reference/architecture + reference/configuration + reference/faq + reference/basic-mapping + reference/association-mapping + reference/inheritance-mapping + reference/working-with-objects + reference/working-with-associations + reference/events + reference/unitofwork + reference/unitofwork-associations + reference/transactions-and-concurrency + reference/batch-processing + reference/dql-doctrine-query-language + reference/query-builder + reference/native-sql + reference/change-tracking-policies + reference/partial-objects + reference/xml-mapping + reference/yaml-mapping + reference/annotations-reference + reference/php-mapping + reference/caching + reference/improving-performance + reference/tools + reference/metadata-drivers + reference/best-practices + reference/limitations-and-known-issues + tutorials/pagination.rst + reference/filters.rst + reference/namingstrategy.rst + + +Cookbook +-------- + +.. toctree:: + :maxdepth: 1 + + cookbook/aggregate-fields + cookbook/decorator-pattern + cookbook/dql-custom-walkers + cookbook/dql-user-defined-functions + cookbook/implementing-arrayaccess-for-domain-objects + cookbook/implementing-the-notify-changetracking-policy + cookbook/implementing-wakeup-or-clone + cookbook/integrating-with-codeigniter + cookbook/sql-table-prefixes + cookbook/strategy-cookbook-introduction + cookbook/validation-of-entities + cookbook/working-with-datetime + cookbook/mysql-enums + cookbook/advanced-field-value-conversion-using-custom-mapping-types + cookbook/entities-in-session + diff --git a/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst new file mode 100644 index 00000000..dd4e49e0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst @@ -0,0 +1,375 @@ +Composite and Foreign Keys as Primary Key +========================================= + +.. versionadded:: 2.1 + +Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept +and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases. +For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as +primary keys are supported. + +This tutorial shows how the semantics of composite primary keys work and how they map to the database. + +General Considerations +~~~~~~~~~~~~~~~~~~~~~~ + +Every entity with a composite key cannot use an id generator other than "ASSIGNED". That means +the ID fields have to have their values set before you call ``EntityManager#persist($entity)``. + +Primitive Types only +~~~~~~~~~~~~~~~~~~~~ + +Even in version 2.0 you can have composite keys as long as they only consist of the primitive types +``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name +and year of production as primary keys: + +.. configuration-block:: + + .. code-block:: php + + name = $name; + $this->year = $year; + } + + public function getModelName() + { + return $this->name; + } + + public function getYearOfProduction() + { + return $this->year; + } + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: yaml + + VehicleCatalogue\Model\Car: + type: entity + id: + name: + type: string + year: + type: integer + +Now you can use this entity: + +.. code-block:: php + + persist($car); + $em->flush(); + +And for querying you can use arrays to both DQL and EntityRepositories: + +.. code-block:: php + + find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010)); + + $dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1"; + $audi = $em->createQuery($dql) + ->setParameter(1, array("name" => "Audi A8", "year" => 2010)) + ->getSingleResult(); + +You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name`` +and to ``year`` to the related entities. + +.. note:: + + This example shows how you can nicely solve the requirement for existing + values before ``EntityManager#persist()``: By adding them as mandatory values for the constructor. + +Identity through foreign Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + Identity through foreign entities is only supported with Doctrine 2.1 + +There are tons of use-cases where the identity of an Entity should be determined by the entity +of one or many parent entities. + +- Dynamic Attributes of an Entity (for example Article). Each Article has many + attributes with primary key "article_id" and "attribute_name". +- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary + key, but the identity is derived through a foreign entity and a foreign key. +- Join Tables with metadata can be modelled as Entity, for example connections between two articles + with a little description and a score. + +The semantics of mapping identity through foreign entities are easy: + +- Only allowed on Many-To-One or One-To-One associations. +- Plug an ``@Id`` annotation onto every association. +- Set an attribute ``association-key`` with the field name of the association in XML. +- Set a key ``associationKey:`` with the field name of the association in YAML. + +Use-Case 1: Dynamic Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We keep up the example of an Article with arbitrary attributes, the mapping looks like this: + +.. configuration-block:: + + .. code-block:: php + + attributes[$name] = new ArticleAttribute($name, $value, $this); + } + } + + /** + * @Entity + */ + class ArticleAttribute + { + /** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */ + private $article; + + /** @Id @Column(type="string") */ + private $attribute; + + /** @Column(type="string") */ + private $value; + + public function __construct($name, $value, $article) + { + $this->attribute = $name; + $this->value = $value; + $this->article = $article; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Application\Model\ArticleAttribute: + type: entity + id: + article: + associationKey: true + attribute: + type: string + fields: + value: + type: string + manyToOne: + article: + targetEntity: Article + inversedBy: attributes + + +Use-Case 2: Simple Derived Identity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you have the requirement that two objects are related by a One-To-One association +and that the dependent class should re-use the primary key of the class it depends on. +One good example for this is a user-address relationship: + +.. configuration-block:: + + .. code-block:: php + + customer = $customer; + $this->items = new ArrayCollection(); + $this->created = new \DateTime("now"); + } + } + + /** @Entity */ + class Product + { + /** @Id @Column(type="integer") @GeneratedValue */ + private $id; + + /** @Column(type="string") */ + private $name; + + /** @Column(type="decimal") */ + private $currentPrice; + + public function getCurrentPrice() + { + return $this->currentPrice; + } + } + + /** @Entity */ + class OrderItem + { + /** @Id @ManyToOne(targetEntity="Order") */ + private $order; + + /** @Id @ManyToOne(targetEntity="Product") */ + private $product; + + /** @Column(type="integer") */ + private $amount = 1; + + /** @Column(type="decimal") */ + private $offeredPrice; + + public function __construct(Order $order, Product $product, $amount = 1) + { + $this->order = $order; + $this->product = $product; + $this->offeredPrice = $product->getCurrentPrice(); + } + } + + +Performance Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using composite keys always comes with a performance hit compared to using entities with +a simple surrogate key. This performance impact is mostly due to additional PHP code that is +necessary to handle this kind of keys, most notably when using derived identifiers. + +On the SQL side there is not much overhead as no additional or unexpected queries have to be +executed to manage entities with derived foreign keys. diff --git a/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst new file mode 100644 index 00000000..7ac88d8c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst @@ -0,0 +1,84 @@ +Extra Lazy Associations +======================= + +.. versionadded:: 2.1 + +In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. +where posts can be commented, you always have to assume that a post draws hundreds of comments. +In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This +can lead to pretty serious performance problems, if your associations contain several hundreds or thousands +of entities. + +With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations +are marked as **Lazy** by default, which means the whole collection object for an association is populated +the first time its accessed. If you mark an association as extra lazy the following methods on collections +can be called without triggering a full load of the collection: + +- ``Collection#contains($entity)`` +- ``Collection#count()`` +- ``Collection#slice($offset, $length = null)`` + +For each of this three methods the following semantics apply: + +- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database. +- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed. + +Additionally even with Doctrine 2.0 the following methods do not trigger the collection load: + +- ``Collection#add($entity)`` +- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does + not work when setting specific keys like ``$coll[0] = $entity``. + +With extra lazy collections you can now not only add entities to large collections but also paginate them +easily using a combination of ``count`` and ``slice``. + + +Enabling Extra-Lazy Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The mapping configuration is simple. Instead of using the default value of ``fetch="LAZY"`` you have to +switch to extra lazy as shown in these examples: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\CMS\CmsGroup: + type: entity + # ... + manyToMany: + users: + targetEntity: CmsUser + mappedBy: groups + fetch: EXTRA_LAZY + diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst new file mode 100644 index 00000000..c625193a --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst @@ -0,0 +1,27 @@ +Getting Started: Database First +=============================== + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +Development of new applications often starts with an existing database schema. +When the database schema is the starting point for your application, then +development is said to use the *Database First* approach to Doctrine. + +In this workflow you would modify the database schema first and then +regenerate the PHP code to use with this schema. You need a flexible +code-generator for this task and up to Doctrine 2.2, the code generator hasn't +been flexible enough to achieve this. + +We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and +allow you to do *Database First* development. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst new file mode 100644 index 00000000..e844b4d6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst @@ -0,0 +1,24 @@ +Getting Started: Model First +============================ + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, then you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +There are applications when you start with a high-level description of the +model using modelling tools such as UML. Modelling tools could also be Excel, +XML or CSV files that describe the model in some structured way. If your +application is using a modelling tool, then the development workflow is said to +be a *Model First* approach to Doctrine2. + +In this workflow you always change the model description and then regenerate +both PHP code and database schema from this model. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst new file mode 100644 index 00000000..1ff52452 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst @@ -0,0 +1,1539 @@ +Getting Started with Doctrine +============================= + +This guide covers getting started with the Doctrine ORM. After working +through the guide you should know: + +- How to install and configure Doctrine by connecting it to a database +- Mapping PHP objects to database tables +- Generating a database schema from PHP objects +- Using the ``EntityManager`` to insert, update, delete and find + objects in the database. + +Guide Assumptions +----------------- + +This guide is designed for beginners that haven't worked with Doctrine ORM +before. There are some prerequesites for the tutorial that have to be +installed: + +- PHP 5.3.3 or above +- Composer Package Manager (`Install Composer + `_) + +The code of this tutorial is `available on Github `_. + +.. note:: + + This tutorial assumes you work with Doctrine 2.3 and above. + Some of the code will not work with lower versions. + +What is Doctrine? +----------------- + +Doctrine 2 is an `object-relational mapper (ORM) +`_ for PHP 5.3.3+ that +provides transparent persistence for PHP objects. It uses the Data Mapper +pattern at the heart, aiming for a complete separation of your domain/business +logic from the persistence in a relational database management system. + +The benefit of Doctrine for the programmer is the ability to focus +on the object-oriented business logic and worry about persistence only +as a secondary problem. This doesn't mean persistence is downplayed by Doctrine +2, however it is our belief that there are considerable benefits for +object-oriented programming if persistence and entities are kept +separated. + +What are Entities? +~~~~~~~~~~~~~~~~~~ + +Entities are PHP Objects that can be identified over many requests +by a unique identifier or primary key. These classes don't need to extend any +abstract base class or interface. An entity class must not be final +or contain final methods. Additionally it must not implement +**clone** nor **wakeup** or :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + +An entity contains persistable properties. A persistable property +is an instance variable of the entity that is saved into and retrieved from the database +by Doctrine's data mapping capabilities. + +An Example Model: Bug Tracker +----------------------------- + +For this Getting Started Guide for Doctrine we will implement the +Bug Tracker domain model from the +`Zend\_Db\_Table `_ +documentation. Reading their documentation we can extract the +requirements: + +- A Bugs has a description, creation date, status, reporter and + engineer +- A bug can occur on different products (platforms) +- Products have a name. +- Bug Reporter and Engineers are both Users of the System. +- A user can create new bugs. +- The assigned engineer can close a bug. +- A user can see all his reported or assigned bugs. +- Bugs can be paginated through a list-view. + +Setup Project +------------- + +Create a new empty folder for this tutorial project, for example +``doctrine2-tutorial`` and create a new file ``composer.json`` with +the following contents: + +:: + + { + "require": { + "doctrine/orm": "2.*", + "symfony/yaml": "2.*" + }, + "autoload": { + "psr-0": {"": "src/"} + } + } + +Install Doctrine using the Composer Dependency Management tool, by calling: + +:: + + $ composer install + +This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM, +Symfony YAML and Symfony Console. Both Symfony dependencies are optional +but will be used in this tutorial. + +You can prepare the directory structure: + +:: + + project + |-- composer.json + |-- config + | |-- xml + | `-- yaml + `-- src + +Obtaining the EntityManager +--------------------------- + +Doctrine's public interface is the EntityManager, it provides the +access point to the complete lifecycle management of your entities +and transforms entities from and back to persistence. You have to +configure and create it to use your entities with Doctrine 2. I +will show the configuration steps and then discuss them step by +step: + +.. code-block:: php + + 'pdo_sqlite', + 'path' => __DIR__ . '/db.sqlite', + ); + + // obtaining the entity manager + $entityManager = EntityManager::create($conn, $config); + +The first require statement sets up the autoloading capabilities of Doctrine +using the Composer autoload. + +The second block consists of the instantiation of the ORM +``Configuration`` object using the Setup helper. It assumes a bunch +of defaults that you don't have to bother about for now. You can +read up on the configuration details in the +:doc:`reference chapter on configuration <../reference/configuration>`. + +The third block shows the configuration options required to connect +to a database, in my case a file-based sqlite database. All the +configuration options for all the shipped drivers are given in the +`DBAL Configuration section of the manual `_. + +The last block shows how the ``EntityManager`` is obtained from a +factory method. + +Generating the Database Schema +------------------------------ + +Now that we have defined the Metadata Mappings and bootstrapped the +EntityManager we want to generate the relational database schema +from it. Doctrine has a Command-Line-Interface that allows you to +access the SchemaTool, a component that generates the required +tables to work with the metadata. + +For the command-line tool to work a cli-config.php file has to be +present in the project root directory, where you will execute the +doctrine command. Its a fairly simple file: + +.. code-block:: php + + new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) + )); + +You can then change into your project directory and call the +Doctrine command-line tool: + +:: + + $ cd project/ + $ php vendor/bin/doctrine orm:schema-tool:create + +During the development you probably need to re-create the database +several times when changing the Entity metadata. You can then +either re-create the database: + +:: + + $ php vendor/bin/doctrine orm:schema-tool:drop --force + $ php vendor/bin/doctrine orm:schema-tool:create + +Or use the update functionality: + +:: + + $ php vendor/bin/doctrine orm:schema-tool:update --force + +The updating of databases uses a Diff Algorithm for a given +Database Schema, a cornerstone of the ``Doctrine\DBAL`` package, +which can even be used without the Doctrine ORM package. However +its not available in SQLite since it does not support ALTER TABLE. + +Starting with the Product +------------------------- + +We start with the Product entity requirements, because it is the most simple one +to get started. Create a ``src/Product.php`` file and put the ``Product`` +entity definition in there: + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +Note how the properties have getter and setter methods defined except +``$id``. To access data from entities Doctrine 2 uses the Reflection API, so it +is possible for Doctrine to access the value of ``$id``. You don't have to +take Doctrine into account when designing access to the state of your objects. + +The next step for persistence with Doctrine is to describe the +structure of the ``Product`` entity to Doctrine using a metadata +language. The metadata language describes how entities, their +properties and references should be persisted and what constraints +should be applied to them. + +Metadata for entities are configured using a XML, YAML or Docblock Annotations. +This Getting Started Guide will show the mappings for all Mapping Drivers. +References in the text will be made to the XML mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Product.dcm.yml + Product: + type: entity + table: products + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + +The top-level ``entity`` definition tag specifies information about +the class and table-name. The primitive type ``Product::$name`` is +defined as ``field`` attributes. The Id property is defined with +the ``id`` tag. The id has a ``generator`` tag nested inside which +defines that the primary key generation mechanism automatically +uses the database platforms native id generation strategy, for +example AUTO INCREMENT in the case of MySql or Sequences in the +case of PostgreSql and Oracle. + +You have to update the database now, because we have a first Entity now: + +:: + + $ php vendor/bin/doctrine orm:schema-tool:update --force --dump-sql + +Specifying both flags ``--force`` and ``-dump-sql`` prints and executes the DDL +statements. + +Now create a new script that will insert products into the database: + +.. code-block:: php + + setName($newProductName); + + $entityManager->persist($product); + $entityManager->flush(); + + echo "Created Product with ID " . $product->getId() . "\n"; + +Call this script from the command line to see how new products are created: + +:: + + $ php create_product.php ORM + $ php create_product.php DBAL + +What is happening here? Using the ``Product`` is pretty standard OOP. +The interesting bits are the use of the ``EntityManager`` service. To +notify the EntityManager that a new entity should be inserted into the database +you have to call ``persist()``. To intiate a transaction to actually perform +the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``. + +This distinction between persist and flush is allows to aggregate all writes +(INSERT, UPDATE, DELETE) into one single transaction, which is executed when +flush is called. Using this approach the write-performance is significantly +better than in a scenario where updates are done for each entity in isolation. + +Doctrine follows the UnitOfWork pattern which additionally detects all entities +that were fetched and have changed during the request. You don't have to keep track of +entities yourself, when Doctrine already knowns about them. + +As a next step we want to fetch a list of all the products. Let's create a +new script for this: + +.. code-block:: php + + getRepository('Product'); + $products = $productRepository->findAll(); + + foreach ($products as $product) { + echo sprintf("-%s\n", $product->getName()); + } + +The ``EntityManager#getRepository()`` method can create a finder object (called +repository) for every entity. It is provided by Doctrine and contains some +finder methods such as ``findAll()``. + +Let's continue with displaying the name of a product based on its ID: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "No product found.\n"; + exit(1); + } + + echo sprintf("-%s\n", $product->getName()); + +Updating a product name demonstrates the functionality UnitOfWork of pattern +discussed before. We only need to find a product entity and all changes to its +properties are written to the database: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $newName = $argv[2]; + + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "Product $id does not exist.\n"; + exit(1); + } + + $product->setName($newName); + + $entityManager->flush(); + +After calling this script on one of the existing products, you can verify the +product name changed by calling the ``show_product.php`` script. + +Adding Bug and User Entities +---------------------------- + +We continue with the bug tracker domain, by creating the missing classes +``Bug`` and ``User`` and putting them into ``src/Bug.php`` and +``src/User.php`` respectively. + +.. code-block:: php + + id; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function setCreated(DateTime $created) + { + $this->created = $created; + } + + public function getCreated() + { + return $this->created; + } + + public function setStatus($status) + { + $this->status = $status; + } + + public function getStatus() + { + return $this->status; + } + } + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +All of the properties discussed so far are simple string and integer values, +for example the id fields of the entities, their names, description, status and +change dates. With just the scalar values this model cannot describe the dynamics that we want. We +want to model references between entities. + +References between objects are foreign keys in the database. You never have to +work with the foreign keys directly, only with objects that represent the +foreign key through their own identity. + +For every foreign key you either have a Doctrine ManyToOne or OneToOne +association. On the inverse sides of these foreign keys you can have +OneToMany associations. Obviously you can have ManyToMany associations +that connect two tables with each other through a join table with +two foreign keys. + +Now that you know the basics about references in Doctrine, we can extend the +domain model to match the requirements: + +.. code-block:: php + + products = new ArrayCollection(); + } + } + +.. code-block:: php + + reportedBugs = new ArrayCollection(); + $this->assignedBugs = new ArrayCollection(); + } + } + +Whenever an entity is recreated from the database, an Collection +implementation of the type Doctrine is injected into your entity +instead of an array. Compared to the ArrayCollection this +implementation helps the Doctrine ORM understand the changes that +have happened to the collection which are noteworthy for +persistence. + +.. warning:: + + Lazy load proxies always contain an instance of + Doctrine's EntityManager and all its dependencies. Therefore a + var\_dump() will possibly dump a very large recursive structure + which is impossible to render and read. You have to use + ``Doctrine\Common\Util\Debug::dump()`` to restrict the dumping to a + human readable level. Additionally you should be aware that dumping + the EntityManager to a Browser may take several minutes, and the + Debug::dump() method just ignores any occurrences of it in Proxy + instances. + +Because we only work with collections for the references we must be +careful to implement a bidirectional reference in the domain model. +The concept of owning or inverse side of a relation is central to +this notion and should always be kept in mind. The following +assumptions are made about relations and have to be followed to be +able to work with Doctrine 2. These assumptions are not unique to +Doctrine 2 but are best practices in handling database relations +and Object-Relational Mapping. + + +- Changes to Collections are saved or updated, when the entity on + the *owning* side of the collection is saved or updated. +- Saving an Entity at the inverse side of a relation never + triggers a persist operation to changes to the collection. +- In a one-to-one relation the entity holding the foreign key of + the related entity on its own database table is *always* the owning + side of the relation. +- In a many-to-many relation, both sides can be the owning side of + the relation. However in a bi-directional many-to-many relation + only one is allowed to be. +- In a many-to-one relation the Many-side is the owning side by + default, because it holds the foreign key. +- The OneToMany side of a relation is inverse by default, since + the foreign key is saved on the Many side. A OneToMany relation can + only be the owning side, if its implemented using a ManyToMany + relation with join table and restricting the one side to allow only + UNIQUE values per database constraint. + +.. note:: + + Consistency of bi-directional references on the inverse side of a + relation have to be managed in userland application code. Doctrine + cannot magically update your collections to be consistent. + + +In the case of Users and Bugs we have references back and forth to +the assigned and reported bugs from a user, making this relation +bi-directional. We have to change the code to ensure consistency of +the bi-directional reference: + +.. code-block:: php + + assignedToBug($this); + $this->engineer = $engineer; + } + + public function setReporter($reporter) + { + $reporter->addReportedBug($this); + $this->reporter = $reporter; + } + + public function getEngineer() + { + return $this->engineer; + } + + public function getReporter() + { + return $this->reporter; + } + } + +.. code-block:: php + + reportedBugs[] = $bug; + } + + public function assignedToBug($bug) + { + $this->assignedBugs[] = $bug; + } + } + +I chose to name the inverse methods in past-tense, which should +indicate that the actual assigning has already taken place and the +methods are only used for ensuring consistency of the references. +This approach is my personal preference, you can choose whatever +method to make this work. + +You can see from ``User::addReportedBug()`` and +``User::assignedToBug()`` that using this method in userland alone +would not add the Bug to the collection of the owning side in +``Bug::$reporter`` or ``Bug::$engineer``. Using these methods and +calling Doctrine for persistence would not update the collections +representation in the database. + +Only using ``Bug::setEngineer()`` or ``Bug::setReporter()`` +correctly saves the relation information. We also set both +collection instance variables to protected, however with PHP 5.3's +new features Doctrine is still able to use Reflection to set and +get values from protected and private properties. + +The ``Bug::$reporter`` and ``Bug::$engineer`` properties are +Many-To-One relations, which point to a User. In a normalized +relational model the foreign key is saved on the Bug's table, hence +in our object-relation model the Bug is at the owning side of the +relation. You should always make sure that the use-cases of your +domain model should drive which side is an inverse or owning one in +your Doctrine mapping. In our example, whenever a new bug is saved +or an engineer is assigned to the bug, we don't want to update the +User to persist the reference, but the Bug. This is the case with +the Bug being at the owning side of the relation. + +Bugs reference Products by an uni-directional ManyToMany relation in +the database that points from Bugs to Products. + +.. code-block:: php + + products[] = $product; + } + + public function getProducts() + { + return $this->products; + } + } + +We are now finished with the domain model given the requirements. +Now we continue adding metadata mappings for the ``User`` and ``Bug`` +as we did for the ``Product`` before: + +.. configuration-block:: + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Bug.dcm.yml + Bug: + type: entity + table: bugs + id: + id: + type: integer + generator: + strategy: AUTO + fields: + description: + type: text + created: + type: datetime + status: + type: string + manyToOne: + reporter: + targetEntity: User + inversedBy: reportedBugs + engineer: + targetEntity: User + inversedBy: assignedBugs + manyToMany: + products: + targetEntity: Product + + +Here we have the entity, id and primitive type definitions. +The column names are used from the Zend\_Db\_Table examples and +have different names than the properties on the Bug class. +Additionally for the "created" field it is specified that it is of +the Type "DATETIME", which translates the YYYY-mm-dd HH:mm:ss +Database format into a PHP DateTime instance and back. + +After the field definitions the two qualified references to the +user entity are defined. They are created by the ``many-to-one`` +tag. The class name of the related entity has to be specified with +the ``target-entity`` attribute, which is enough information for +the database mapper to access the foreign-table. Since +``reporter`` and ``engineer`` are on the owning side of a +bi-directional relation we also have to specify the ``inversed-by`` +attribute. They have to point to the field names on the inverse +side of the relationship. We will see in the next example that the ``inversed-by`` +attribute has a counterpart ``mapped-by`` which makes that +the inverse side. + +The last missing property is the ``Bug::$products`` collection. It +holds all products where the specific bug is occurring in. Again +you have to define the ``target-entity`` and ``field`` attributes +on the ``many-to-many`` tag. Furthermore you have to specify the +details of the many-to-many join-table and its foreign key columns. +The definition is rather complex, however relying on the XML +auto-completion I got it working easily, although I forget the +schema details all the time. + +The last missing definition is that of the User entity: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/xml/User.dcm.yml + User: + type: entity + table: users + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + oneToMany: + reportedBugs: + targetEntity: Bug + mappedBy: reporter + assignedBugs: + targetEntity: Bug + mappedBy: engineer + +Here are some new things to mention about the ``one-to-many`` tags. +Remember that we discussed about the inverse and owning side. Now +both reportedBugs and assignedBugs are inverse relations, which +means the join details have already been defined on the owning +side. Therefore we only have to specify the property on the Bug +class that holds the owning sides. + +This example has a fair overview of the most basic features of the +metadata definition language. + +Implementing more Requirements +------------------------------ + +For starters we need a create user entities: + +.. code-block:: php + + setName($newUsername); + + $entityManager->persist($user); + $entityManager->flush(); + + echo "Created User with ID " . $user->getId() . "\n"; + +Now call: + +:: + + $ php create_user.php beberlei + +We now have the data to create a bug and the code for this scenario may look +like this: + +.. code-block:: php + + find("User", $theReporterId); + $engineer = $entityManager->find("User", $theDefaultEngineerId); + if (!$reporter || !$engineer) { + echo "No reporter and/or engineer found for the input.\n"; + exit(1); + } + + $bug = new Bug(); + $bug->setDescription("Something does not work!"); + $bug->setCreated(new DateTime("now")); + $bug->setStatus("OPEN"); + + foreach ($productIds AS $productId) { + $product = $entityManager->find("Product", $productId); + $bug->assignToProduct($product); + } + + $bug->setReporter($reporter); + $bug->setEngineer($engineer); + + $entityManager->persist($bug); + $entityManager->flush(); + + echo "Your new Bug Id: ".$bug->getId()."\n"; + +Since we only have one user and product, probably with the ID of 1, we can call this script with: + +:: + + php create_bug.php 1 1 1 + +This is the first contact with the read API of the EntityManager, +showing that a call to ``EntityManager#find($name, $id)`` returns a +single instance of an entity queried by primary key. Besides this +we see the persist + flush pattern again to save the Bug into the +database. + +See how simple relating Bug, Reporter, Engineer and Products is +done by using the discussed methods in the "A first prototype" +section. The UnitOfWork will detect this relations when flush is +called and relate them in the database appropriately. + +Queries for Application Use-Cases +--------------------------------- + +List of Bugs +~~~~~~~~~~~~ + +Using the previous examples we can fill up the database quite a +bit, however we now need to discuss how to query the underlying +mapper for the required view representations. When opening the +application, bugs can be paginated through a list-view, which is +the first read-only use-case: + +.. code-block:: php + + createQuery($dql); + $query->setMaxResults(30); + $bugs = $query->getResult(); + + foreach($bugs AS $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->name."\n"; + echo " Assigned to: ".$bug->getEngineer()->name."\n"; + foreach($bug->getProducts() AS $product) { + echo " Platform: ".$product->name."\n"; + } + echo "\n"; + } + +The DQL Query in this example fetches the 30 most recent bugs with +their respective engineer and reporter in one single SQL statement. +The console output of this script is then: + +:: + + Something does not work! - 02.04.2010 + Reported by: beberlei + Assigned to: beberlei + Platform: My Product + +.. note:: + + **Dql is not Sql** + + You may wonder why we start writing SQL at the beginning of this + use-case. Don't we use an ORM to get rid of all the endless + hand-writing of SQL? Doctrine introduces DQL which is best + described as **object-query-language** and is a dialect of + `OQL `_ and + similar to `HQL `_ or + `JPQL `_. + It does not know the concept of columns and tables, but only those + of Entity-Class and property. Using the Metadata we defined before + it allows for very short distinctive and powerful queries. + + An important reason why DQL is favourable to the Query API of most + ORMs is its similarity to SQL. The DQL language allows query + constructs that most ORMs don't, GROUP BY even with HAVING, + Sub-selects, Fetch-Joins of nested classes, mixed results with + entities and scalar data such as COUNT() results and much more. + Using DQL you should seldom come to the point where you want to + throw your ORM into the dumpster, because it doesn't support some + the more powerful SQL concepts. + + Besides handwriting DQL you can however also use the + ``QueryBuilder`` retrieved by calling + ``$entityManager->createQueryBuilder()`` which is a Query Object + around the DQL language. + + As a last resort you can however also use Native SQL and a + description of the result set to retrieve entities from the + database. DQL boils down to a Native SQL statement and a + ``ResultSetMapping`` instance itself. Using Native SQL you could + even use stored procedures for data retrieval, or make use of + advanced non-portable database queries like PostgreSql's recursive + queries. + + +Array Hydration of the Bug List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the previous use-case we retrieved the result as their +respective object instances. We are not limited to retrieving +objects only from Doctrine however. For a simple list view like the +previous one we only need read access to our entities and can +switch the hydration from objects to simple PHP arrays instead. +This can obviously yield considerable performance benefits for +read-only requests. + +Implementing the same list view as before using array hydration we +can rewrite our code: + +.. code-block:: php + + createQuery($dql); + $bugs = $query->getArrayResult(); + + foreach ($bugs AS $bug) { + echo $bug['description'] . " - " . $bug['created']->format('d.m.Y')."\n"; + echo " Reported by: ".$bug['reporter']['name']."\n"; + echo " Assigned to: ".$bug['engineer']['name']."\n"; + foreach($bug['products'] AS $product) { + echo " Platform: ".$product['name']."\n"; + } + echo "\n"; + } + +There is one significant difference in the DQL query however, we +have to add an additional fetch-join for the products connected to +a bug. The resulting SQL query for this single select statement is +pretty large, however still more efficient to retrieve compared to +hydrating objects. + +Find by Primary Key +~~~~~~~~~~~~~~~~~~~ + +The next Use-Case is displaying a Bug by primary key. This could be +done using DQL as in the previous example with a where clause, +however there is a convenience method on the ``EntityManager`` that +handles loading by primary key, which we have already seen in the +write scenarios: + +.. code-block:: php + + find("Bug", (int)$theBugId); + + echo "Bug: ".$bug->getDescription()."\n"; + echo "Engineer: ".$bug->getEngineer()->getName()."\n"; + +The output of the engineers name is fetched from the database! What is happening? + +Since we only retrieved the bug by primary key both the engineer and reporter +are not immediately loaded from the database but are replaced by LazyLoading +proxies. These proxies will load behind the scenes, when the first method +is called on them. + +Sample code of this proxy generated code can be found in the specified Proxy +Directory, it looks like: + +.. code-block:: php + + _load(); + return parent::addReportedBug($bug); + } + + public function assignedToBug($bug) + { + $this->_load(); + return parent::assignedToBug($bug); + } + } + +See how upon each method call the proxy is lazily loaded from the +database? + +The call prints: + +:: + + $ php show_bug.php 1 + Bug: Something does not work! + Engineer: beberlei + +Dashboard of the User +--------------------- + +For the next use-case we want to retrieve the dashboard view, a +list of all open bugs the user reported or was assigned to. This +will be achieved using DQL again, this time with some WHERE clauses +and usage of bound parameters: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $theUserId) + ->setMaxResults(15) + ->getResult(); + + echo "You have created or assigned to " . count($myBugs) . " open bugs:\n\n"; + + foreach ($myBugs AS $bug) { + echo $bug->getId() . " - " . $bug->getDescription()."\n"; + } + +Number of Bugs +-------------- + +Until now we only retrieved entities or their array representation. +Doctrine also supports the retrieval of non-entities through DQL. +These values are called "scalar result values" and may even be +aggregate values using COUNT, SUM, MIN, MAX or AVG functions. + +We will need this knowledge to retrieve the number of open bugs +grouped by product: + +.. code-block:: php + + createQuery($dql)->getScalarResult(); + + foreach($productBugs as $productBug) { + echo $productBug['name']." has " . $productBug['openBugs'] . " open bugs!\n"; + } + +Updating Entities +----------------- + +There is a single use-case missing from the requirements, Engineers +should be able to close a bug. This looks like: + +.. code-block:: php + + status = "CLOSE"; + } + } + +.. code-block:: php + + find("Bug", (int)$theBugId); + $bug->close(); + + $entityManager->flush(); + +When retrieving the Bug from the database it is inserted into the +IdentityMap inside the UnitOfWork of Doctrine. This means your Bug +with exactly this id can only exist once during the whole request +no matter how often you call ``EntityManager#find()``. It even +detects entities that are hydrated using DQL and are already +present in the Identity Map. + +When flush is called the EntityManager loops over all the entities +in the identity map and performs a comparison between the values +originally retrieved from the database and those values the entity +currently has. If at least one of these properties is different the +entity is scheduled for an UPDATE against the database. Only the +changed columns are updated, which offers a pretty good performance +improvement compared to updating all the properties. + +Entity Repositories +------------------- + +For now we have not discussed how to separate the Doctrine query logic from your model. +In Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this +separation. The similar concept in Doctrine2 is called Entity Repositories, integrating +the `repository pattern `_ at the heart of Doctrine. + +Every Entity uses a default repository by default and offers a bunch of convenience +methods that you can use to query for instances of that Entity. Take for example +our Product entity. If we wanted to Query by name, we can use: + +.. code-block:: php + + getRepository('Product') + ->findOneBy(array('name' => $productName)); + +The method ``findOneBy()`` takes an array of fields or association keys and the values to match against. + +If you want to find all entities matching a condition you can use ``findBy()``, for +example querying for all closed bugs: + +.. code-block:: php + + getRepository('Bug') + ->findBy(array('status' => 'CLOSED')); + + foreach ($bugs AS $bug) { + // do stuff + } + +Compared to DQL these query methods are falling short of functionality very fast. +Doctrine offers you a convenient way to extend the functionalities of the default ``EntityRepository`` +and put all the specialized DQL query logic on it. For this you have to create a subclass +of ``Doctrine\ORM\EntityRepository``, in our case a ``BugRepository`` and group all +the previously discussed query functionality in it: + +.. code-block:: php + + getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getResult(); + } + + public function getRecentBugsArray($number = 30) + { + $dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e ". + "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC"; + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getArrayResult(); + } + + public function getUsersBugs($userId, $number = 15) + { + $dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ". + "WHERE b.status = 'OPEN' AND e.id = ?1 OR r.id = ?1 ORDER BY b.created DESC"; + + return $this->getEntityManager()->createQuery($dql) + ->setParameter(1, $userId) + ->setMaxResults($number) + ->getResult(); + } + + public function getOpenBugsByProduct() + { + $dql = "SELECT p.id, p.name, count(b.id) AS openBugs FROM Bug b ". + "JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id"; + return $this->getEntityManager()->createQuery($dql)->getScalarResult(); + } + } + +Don't forget to add a `require_once` call for this class to the bootstrap.php + +To be able to use this query logic through ``$this->getEntityManager()->getRepository('Bug')`` +we have to adjust the metadata slightly. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + .. code-block:: yaml + + Bug: + type: entity + repositoryClass: BugRepository + +Now we can remove our query logic in all the places and instead use them through the EntityRepository. +As an example here is the code of the first use case "List of Bugs": + +.. code-block:: php + + getRepository('Bug')->getRecentBugs(); + + foreach($bugs AS $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; + foreach($bug->getProducts() AS $product) { + echo " Platform: ".$product->getName()."\n"; + } + echo "\n"; + } + +Using EntityRepositories you can avoid coupling your model with specific query logic. +You can also re-use query logic easily throughout your application. + +Conclusion +---------- + +This tutorial is over here, I hope you had fun. Additional content +will be added to this tutorial incrementally, topics will include: + +- More on Association Mappings +- Lifecycle Events triggered in the UnitOfWork +- Ordering of Collections + +Additional details on all the topics discussed here can be found in +the respective manual chapters. + diff --git a/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst new file mode 100644 index 00000000..121bd145 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst @@ -0,0 +1,110 @@ +Ordering To-Many Associations +----------------------------- + +There are use-cases when you'll want to sort collections when they are +retrieved from the database. In userland you do this as long as you +haven't initially saved an entity with its associations into the +database. To retrieve a sorted collection from the database you can +use the ``@OrderBy`` annotation with an collection that specifies +an DQL snippet that is appended to all queries with this +collection. + +Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you +can specify the ``@OrderBy`` in the following way: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + orderBy: { 'name': 'ASC' } + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +The DQL Snippet in OrderBy is only allowed to consist of +unqualified, unquoted field names and of an optional ASC/DESC +positional statement. Multiple Fields are separated by a comma (,). +The referenced field names have to exist on the ``targetEntity`` +class of the ``@ManyToMany`` or ``@OneToMany`` annotation. + +The semantics of this feature can be described as follows. + + +- ``@OrderBy`` acts as an implicit ORDER BY clause for the given + fields, that is appended to all the explicitly given ORDER BY + items. +- All collections of the ordered type are always retrieved in an + ordered fashion. +- To keep the database impact low, these implicit ORDER BY items + are only added to an DQL Query if the collection is fetch joined in + the DQL query. + +Given our previously defined example, the following would not add +ORDER BY, since g is not fetch joined: + +.. code-block:: sql + + SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10 + +However the following: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 + +...would internally be rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC + +You can reverse the order with an explicit DQL ORDER BY: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC + +...is internally rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC + + diff --git a/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst new file mode 100644 index 00000000..792d9788 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -0,0 +1,90 @@ +Override Field Association Mappings In Subclasses +------------------------------------------------- + +Sometimes there is a need to persist entities but override all or part of the +mapping metadata. Sometimes also the mapping to override comes from entities +using traits where the traits have mapping metadata. +This tutorial explains how to override mapping metadata, +i.e. attributes and associations metadata in particular. The example here shows +the overriding of a class that uses a trait but is similar when extending a base +class as shown at the end of this tutorial. + +Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait: + +.. code-block:: php + + createQuery($dql) + ->setFirstResult(0) + ->setMaxResults(100); + + $paginator = new Paginator($query, $fetchJoinCollection = true); + + $c = count($paginator); + foreach ($paginator as $post) { + echo $post->getHeadline() . "\n"; + } + +Paginating Doctrine queries is not as simple as you might think in the +beginning. If you have complex fetch-join scenarios with one-to-many or +many-to-many associations using the "default" LIMIT functionality of database +vendors is not sufficient to get the correct results. + +By default the pagination extension does the following steps to compute the +correct result: + +- Perform a Count query using `DISTINCT` keyword. +- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page. +- Perform a WHERE IN query to get all results for the current page. + +This behavior is only necessary if you actually fetch join a to-many +collection. You can disable this behavior by setting the +``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries +described are executed. We hope to automate the detection for this in +the future. diff --git a/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst new file mode 100644 index 00000000..3536a34f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst @@ -0,0 +1,298 @@ +Working with Indexed Associations +================================= + +.. note:: + + This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series. + +Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in +the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY`` +was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity. +This is a first step towards full ordered hashmap support through the Doctrine ORM. +The feature works like an implicit ``INDEX BY`` for the selected association but has several +downsides also: + +- You have to manage both the key and field if you want to change the index by field value. +- On each request the keys are regenerated from the field value not from the previous collection key. +- Values of the Index-By keys are never considered during persistence, it only exists for accessing purposes. +- Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities + with the same index-by field value is undefined. + +As an example we will design a simple stock exchange list view. The domain consists of the entity ``Stock`` +and ``Market`` where each Stock has a symbol and is traded on a single market. Instead of having a numerical +list of stocks traded on a market they will be indexed by their symbol, which is unique across all markets. + +Mapping Indexed Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can map indexed associations by adding: + + * ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation. + * ``index-by`` attribute to any ```` or ```` xml element. + * ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files. + +The code and mappings for the Market entity looks like this: + +.. configuration-block:: + .. code-block:: php + + name = $name; + $this->stocks = new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function addStock(Stock $stock) + { + $this->stocks[$stock->getSymbol()] = $stock; + } + + public function getStock($symbol) + { + if (!isset($this->stocks[$symbol])) { + throw new \InvalidArgumentException("Symbol is not traded on this market."); + } + + return $this->stocks[$symbol]; + } + + public function getStocks() + { + return $this->stocks->toArray(); + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Market: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type:string + oneToMany: + stocks: + targetEntity: Stock + mappedBy: market + indexBy: symbol + +Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol, +so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)`` +we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown. + +The ``Stock`` entity doesn't contain any special instructions that are new, but for completeness +here are the code and mappings for it: + +.. configuration-block:: + .. code-block:: php + + symbol = $symbol; + $this->market = $market; + $market->addStock($this); + } + + public function getSymbol() + { + return $this->symbol; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Stock: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + symbol: + type: string + manyToOne: + market: + targetEntity: Market + inversedBy: stocks + +Querying indexed associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we defined the stocks collection to be indexed by symbol we can take a look at some code, +that makes use of the indexing. + +First we will populate our database with two example stocks traded on a single market: + +.. code-block:: php + + persist($market); + $em->persist($stock1); + $em->persist($stock2); + $em->flush(); + +This code is not particular interesting since the indexing feature is not yet used. In a new request we could +now query for the market: + +.. code-block:: php + + find("Doctrine\Tests\Models\StockExchange\Market", $marketId); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +The implementation ``Market::addStock()`` in combination with ``indexBy`` allows to access the collection +consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not. + +The same applies to DQL queries: The ``indexBy`` configuration acts as implicit "INDEX BY" to a join association. + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $marketId) + ->getSingleResult(); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally +indexed associations also work with the ``Collection::slice()`` functionality, no matter if marked as +LAZY or EXTRA_LAZY. + +Outlook into the Future +~~~~~~~~~~~~~~~~~~~~~~~ + +For the inverse side of a many-to-many associations there will be a way to persist the keys and the order +as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 `_ +This feature cannot be implemented for One-To-Many associations, because they are never the owning side. + diff --git a/vendor/doctrine/orm/doctrine-mapping.xsd b/vendor/doctrine/orm/doctrine-mapping.xsd new file mode 100644 index 00000000..f9c774d5 --- /dev/null +++ b/vendor/doctrine/orm/doctrine-mapping.xsd @@ -0,0 +1,563 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php new file mode 100644 index 00000000..b3231051 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php @@ -0,0 +1,872 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\ORMInvalidArgumentException; + +/** + * Base contract for ORM queries. Base class for Query and NativeQuery. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + */ +abstract class AbstractQuery +{ + /* Hydration mode constants */ + + /** + * Hydrates an object graph. This is the default behavior. + */ + const HYDRATE_OBJECT = 1; + + /** + * Hydrates an array graph. + */ + const HYDRATE_ARRAY = 2; + + /** + * Hydrates a flat, rectangular result set with scalar values. + */ + const HYDRATE_SCALAR = 3; + + /** + * Hydrates a single scalar value. + */ + const HYDRATE_SINGLE_SCALAR = 4; + + /** + * Very simple object hydrator (optimized for performance). + */ + const HYDRATE_SIMPLEOBJECT = 5; + + /** + * The parameter map of this query. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + protected $parameters; + + /** + * The user-specified ResultSetMapping to use. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_resultSetMapping; + + /** + * The entity manager used by this query object. + * + * @var \Doctrine\ORM\EntityManager + */ + protected $_em; + + /** + * The map of query hints. + * + * @var array + */ + protected $_hints = array(); + + /** + * The hydration mode. + * + * @var integer + */ + protected $_hydrationMode = self::HYDRATE_OBJECT; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_queryCacheProfile; + + /** + * Whether or not expire the result cache. + * + * @var boolean + */ + protected $_expireResultCache = false; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_hydrationCacheProfile; + + /** + * Initializes a new instance of a class derived from AbstractQuery. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + } + + /** + * Gets the SQL query that corresponds to this query object. + * The returned SQL syntax depends on the connection driver that is used + * by this query object at the time of this method call. + * + * @return string SQL query + */ + abstract public function getSQL(); + + /** + * Retrieves the associated EntityManager of this Query instance. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Frees the resources used by the query object. + * + * Resets Parameters, Parameter Types and Query Hints. + * + * @return void + */ + public function free() + { + $this->parameters = new ArrayCollection(); + + $this->_hints = array(); + } + + /** + * Get all defined parameters. + * + * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a query parameter. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : null; + } + + /** + * Sets a collection of query parameters. + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameter = new Query\Parameter($key, $value); + + $parameterCollection->add($parameter); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Sets a query parameter. + * + * @param string|int $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setParameter($key, $value, $type = null) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $parameter = new Query\Parameter($key, $value, $type); + + $this->parameters->add($parameter); + + return $this; + } + + /** + * Processes an individual parameter value. + * + * @param mixed $value + * + * @return array + * + * @throws ORMInvalidArgumentException + */ + public function processParameterValue($value) + { + if (is_array($value)) { + foreach ($value as $key => $paramValue) { + $paramValue = $this->processParameterValue($paramValue); + $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue; + } + + return $value; + } + + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value); + + if ($value === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + if ($value instanceof Mapping\ClassMetadata) { + return $value->name; + } + + return $value; + } + + /** + * Sets the ResultSetMapping that should be used for hydration. + * + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultSetMapping(Query\ResultSetMapping $rsm) + { + $this->translateNamespaces($rsm); + $this->_resultSetMapping = $rsm; + + return $this; + } + + /** + * Allows to translate entity namespaces to full qualified names. + * + * @param Query\ResultSetMapping $rsm + * + * @return void + */ + private function translateNamespaces(Query\ResultSetMapping $rsm) + { + $entityManager = $this->_em; + + $translate = function ($alias) use ($entityManager) { + return $entityManager->getClassMetadata($alias)->getName(); + }; + + $rsm->aliasMap = array_map($translate, $rsm->aliasMap); + $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses); + } + + /** + * Set a cache profile for hydration caching. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * Important: Hydration caching does NOT register entities in the + * UnitOfWork when retrieved from the cache. Never use result cached + * entities for requests that also flush the EntityManager. If you want + * some form of caching with UnitOfWork registration you should use + * {@see AbstractQuery::setResultCacheProfile()}. + * + * @example + * $lifetime = 100; + * $resultKey = "abc"; + * $query->setHydrationCacheProfile(new QueryCacheProfile()); + * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setHydrationCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_hydrationCacheProfile = $profile; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function getHydrationCacheProfile() + { + return $this->_hydrationCacheProfile; + } + + /** + * Set a cache profile for the result cache. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_queryCacheProfile = $profile; + + return $this; + } + + /** + * Defines a cache driver to be used for caching result sets and implicitly enables caching. + * + * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver + * + * @return \Doctrine\ORM\AbstractQuery + * + * @throws ORMException + */ + public function setResultCacheDriver($resultCacheDriver = null) + { + if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { + throw ORMException::invalidResultCacheDriver(); + } + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) + : new QueryCacheProfile(0, null, $resultCacheDriver); + + return $this; + } + + /** + * Returns the cache driver used for caching result sets. + * + * @deprecated + * + * @return \Doctrine\Common\Cache\Cache Cache driver + */ + public function getResultCacheDriver() + { + if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { + return $this->_queryCacheProfile->getResultCacheDriver(); + } + + return $this->_em->getConfiguration()->getResultCacheImpl(); + } + + /** + * Set whether or not to cache the results of this query and if so, for + * how long and which ID to use for the cache entry. + * + * @param boolean $bool + * @param integer $lifetime + * @param string $resultCacheId + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function useResultCache($bool, $lifetime = null, $resultCacheId = null) + { + if ($bool) { + $this->setResultCacheLifetime($lifetime); + $this->setResultCacheId($resultCacheId); + + return $this; + } + + $this->_queryCacheProfile = null; + + return $this; + } + + /** + * Defines how long the result cache will be active before expire. + * + * @param integer $lifetime How long the cache entry is valid. + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setResultCacheLifetime($lifetime) + { + $lifetime = ($lifetime !== null) ? (int) $lifetime : 0; + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setLifetime($lifetime) + : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @deprecated + * + * @return integer + */ + public function getResultCacheLifetime() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0; + } + + /** + * Defines if the result cache is active or not. + * + * @param boolean $expire Whether or not to force resultset cache expiration. + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function expireResultCache($expire = true) + { + $this->_expireResultCache = $expire; + + return $this; + } + + /** + * Retrieves if the resultset cache is active or not. + * + * @return boolean + */ + public function getExpireResultCache() + { + return $this->_expireResultCache; + } + + /** + * @return QueryCacheProfile + */ + public function getQueryCacheProfile() + { + return $this->_queryCacheProfile; + } + + /** + * Change the default fetch mode of an association for this query. + * + * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY + * + * @param string $class + * @param string $assocName + * @param int $fetchMode + * + * @return AbstractQuery + */ + public function setFetchMode($class, $assocName, $fetchMode) + { + if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) { + $fetchMode = Mapping\ClassMetadata::FETCH_LAZY; + } + + $this->_hints['fetchMode'][$class][$assocName] = $fetchMode; + + return $this; + } + + /** + * Defines the processing mode to be used during hydration / result set transformation. + * + * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. + * One of the Query::HYDRATE_* constants. + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setHydrationMode($hydrationMode) + { + $this->_hydrationMode = $hydrationMode; + + return $this; + } + + /** + * Gets the hydration mode currently used by the query. + * + * @return integer + */ + public function getHydrationMode() + { + return $this->_hydrationMode; + } + + /** + * Gets the list of results for the query. + * + * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT). + * + * @param int $hydrationMode + * + * @return array + */ + public function getResult($hydrationMode = self::HYDRATE_OBJECT) + { + return $this->execute(null, $hydrationMode); + } + + /** + * Gets the array of results for the query. + * + * Alias for execute(null, HYDRATE_ARRAY). + * + * @return array + */ + public function getArrayResult() + { + return $this->execute(null, self::HYDRATE_ARRAY); + } + + /** + * Gets the scalar results for the query. + * + * Alias for execute(null, HYDRATE_SCALAR). + * + * @return array + */ + public function getScalarResult() + { + return $this->execute(null, self::HYDRATE_SCALAR); + } + + /** + * Get exactly one result or null. + * + * @param int $hydrationMode + * + * @return mixed + * + * @throws NonUniqueResultException + */ + public function getOneOrNullResult($hydrationMode = null) + { + $result = $this->execute(null, $hydrationMode); + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + return null; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single result of the query. + * + * Enforces the presence as well as the uniqueness of the result. + * + * If the result is not unique, a NonUniqueResultException is thrown. + * If there is no result, a NoResultException is thrown. + * + * @param integer $hydrationMode + * + * @return mixed + * + * @throws NonUniqueResultException If the query result is not unique. + * @throws NoResultException If the query returned no result. + */ + public function getSingleResult($hydrationMode = null) + { + $result = $this->execute(null, $hydrationMode); + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + throw new NoResultException; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single scalar result of the query. + * + * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). + * + * @return mixed + * + * @throws QueryException If the query result is not unique. + */ + public function getSingleScalarResult() + { + return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); + } + + /** + * Sets a query hint. If the hint name is not recognized, it is silently ignored. + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setHint($name, $value) + { + $this->_hints[$name] = $value; + + return $this; + } + + /** + * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. + * + * @param string $name The name of the hint. + * + * @return mixed The value of the hint or FALSE, if the hint name is not recognized. + */ + public function getHint($name) + { + return isset($this->_hints[$name]) ? $this->_hints[$name] : false; + } + + /** + * Return the key value map of query hints that are currently set. + * + * @return array + */ + public function getHints() + { + return $this->_hints; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterate over the result. + * + * @param ArrayCollection|array|null $parameters The query parameters. + * @param integer|null $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $stmt = $this->_doExecute(); + + return $this->_em->newHydrator($this->_hydrationMode)->iterate( + $stmt, $this->_resultSetMapping, $this->_hints + ); + } + + /** + * Executes the query. + * + * @param ArrayCollection|array|null $parameters Query parameters. + * @param integer|null $hydrationMode Processing mode to be used during the hydration process. + * + * @return mixed + */ + public function execute($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $setCacheEntry = function() {}; + + if ($this->_hydrationCacheProfile !== null) { + list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); + + $queryCacheProfile = $this->getHydrationCacheProfile(); + $cache = $queryCacheProfile->getResultCacheDriver(); + $result = $cache->fetch($cacheKey); + + if (isset($result[$realCacheKey])) { + return $result[$realCacheKey]; + } + + if ( ! $result) { + $result = array(); + } + + $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { + $result[$realCacheKey] = $data; + + $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); + }; + } + + $stmt = $this->_doExecute(); + + if (is_numeric($stmt)) { + $setCacheEntry($stmt); + + return $stmt; + } + + $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( + $stmt, $this->_resultSetMapping, $this->_hints + ); + + $setCacheEntry($data); + + return $data; + } + + /** + * Get the result cache id to use to store the result set cache entry. + * Will return the configured id if it exists otherwise a hash will be + * automatically generated for you. + * + * @return array ($key, $hash) + */ + protected function getHydrationCacheId() + { + $parameters = array(); + + foreach ($this->getParameters() as $parameter) { + $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue()); + } + + $sql = $this->getSQL(); + $queryCacheProfile = $this->getHydrationCacheProfile(); + $hints = $this->getHints(); + $hints['hydrationMode'] = $this->getHydrationMode(); + + ksort($hints); + + return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints); + } + + /** + * Set the result cache id to use to store the result set cache entry. + * If this is not explicitly set by the developer then a hash is automatically + * generated for you. + * + * @param string $id + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setResultCacheId($id) + { + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setCacheKey($id) + : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Get the result cache id to use to store the result set cache entry if set. + * + * @deprecated + * + * @return string + */ + public function getResultCacheId() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null; + } + + /** + * Executes the query and returns a the resulting Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results. + */ + abstract protected function _doExecute(); + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + $this->parameters = new ArrayCollection(); + + $this->_hints = array(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php new file mode 100644 index 00000000..959a206d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php @@ -0,0 +1,782 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\QuoteStrategy; +use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\NamingStrategy; +use Doctrine\ORM\Mapping\DefaultNamingStrategy; +use Doctrine\ORM\Mapping\EntityListenerResolver; +use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; +use Doctrine\Common\Annotations\SimpleAnnotationReader; +use Doctrine\Common\Annotations\CachedReader; + +/** + * Configuration container for all configuration options of Doctrine. + * It combines all configuration options from DBAL & ORM. + * + * @since 2.0 + * @internal When adding a new configuration option just write a getter/setter pair. + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Configuration extends \Doctrine\DBAL\Configuration +{ + /** + * Sets the directory where Doctrine generates any necessary proxy class files. + * + * @param string $dir + * + * @return void + */ + public function setProxyDir($dir) + { + $this->_attributes['proxyDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates any necessary proxy class files. + * + * @return string|null + */ + public function getProxyDir() + { + return isset($this->_attributes['proxyDir']) + ? $this->_attributes['proxyDir'] + : null; + } + + /** + * Gets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @return boolean + */ + public function getAutoGenerateProxyClasses() + { + return isset($this->_attributes['autoGenerateProxyClasses']) + ? $this->_attributes['autoGenerateProxyClasses'] + : true; + } + + /** + * Sets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @param boolean $bool + * + * @return void + */ + public function setAutoGenerateProxyClasses($bool) + { + $this->_attributes['autoGenerateProxyClasses'] = $bool; + } + + /** + * Gets the namespace where proxy classes reside. + * + * @return string|null + */ + public function getProxyNamespace() + { + return isset($this->_attributes['proxyNamespace']) + ? $this->_attributes['proxyNamespace'] + : null; + } + + /** + * Sets the namespace where proxy classes reside. + * + * @param string $ns + * + * @return void + */ + public function setProxyNamespace($ns) + { + $this->_attributes['proxyNamespace'] = $ns; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param MappingDriver $driverImpl + * + * @return void + * + * @todo Force parameter to be a Closure to ensure lazy evaluation + * (as soon as a metadata cache is in effect, the driver never needs to initialize). + */ + public function setMetadataDriverImpl(MappingDriver $driverImpl) + { + $this->_attributes['metadataDriverImpl'] = $driverImpl; + } + + /** + * Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader + * is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported. + * + * @param array $paths + * @param bool $useSimpleAnnotationReader + * + * @return AnnotationDriver + */ + public function newDefaultAnnotationDriver($paths = array(), $useSimpleAnnotationReader = true) + { + AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); + + if ($useSimpleAnnotationReader) { + // Register the ORM Annotations in the AnnotationRegistry + $reader = new SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $cachedReader = new CachedReader($reader, new ArrayCache()); + + return new AnnotationDriver($cachedReader, (array) $paths); + } + + return new AnnotationDriver( + new CachedReader(new AnnotationReader(), new ArrayCache()), + (array) $paths + ); + } + + /** + * Adds a namespace under a certain alias. + * + * @param string $alias + * @param string $namespace + * + * @return void + */ + public function addEntityNamespace($alias, $namespace) + { + $this->_attributes['entityNamespaces'][$alias] = $namespace; + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * @param string $entityNamespaceAlias + * + * @return string + * + * @throws ORMException + */ + public function getEntityNamespace($entityNamespaceAlias) + { + if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) { + throw ORMException::unknownEntityNamespace($entityNamespaceAlias); + } + + return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\'); + } + + /** + * Sets the entity alias map. + * + * @param array $entityNamespaces + * + * @return void + */ + public function setEntityNamespaces(array $entityNamespaces) + { + $this->_attributes['entityNamespaces'] = $entityNamespaces; + } + + /** + * Retrieves the list of registered entity namespace aliases. + * + * @return array + */ + public function getEntityNamespaces() + { + return $this->_attributes['entityNamespaces']; + } + + /** + * Gets the cache driver implementation that is used for the mapping metadata. + * + * @return MappingDriver|null + * + * @throws ORMException + */ + public function getMetadataDriverImpl() + { + return isset($this->_attributes['metadataDriverImpl']) + ? $this->_attributes['metadataDriverImpl'] + : null; + } + + /** + * Gets the cache driver implementation that is used for the query cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getQueryCacheImpl() + { + return isset($this->_attributes['queryCacheImpl']) + ? $this->_attributes['queryCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the query cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setQueryCacheImpl(Cache $cacheImpl) + { + $this->_attributes['queryCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getHydrationCacheImpl() + { + return isset($this->_attributes['hydrationCacheImpl']) + ? $this->_attributes['hydrationCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setHydrationCacheImpl(Cache $cacheImpl) + { + $this->_attributes['hydrationCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for metadata caching. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getMetadataCacheImpl() + { + return isset($this->_attributes['metadataCacheImpl']) + ? $this->_attributes['metadataCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setMetadataCacheImpl(Cache $cacheImpl) + { + $this->_attributes['metadataCacheImpl'] = $cacheImpl; + } + + /** + * Adds a named DQL query to the configuration. + * + * @param string $name The name of the query. + * @param string $dql The DQL query string. + * + * @return void + */ + public function addNamedQuery($name, $dql) + { + $this->_attributes['namedQueries'][$name] = $dql; + } + + /** + * Gets a previously registered named DQL query. + * + * @param string $name The name of the query. + * + * @return string The DQL query. + * + * @throws ORMException + */ + public function getNamedQuery($name) + { + if ( ! isset($this->_attributes['namedQueries'][$name])) { + throw ORMException::namedQueryNotFound($name); + } + + return $this->_attributes['namedQueries'][$name]; + } + + /** + * Adds a named native query to the configuration. + * + * @param string $name The name of the query. + * @param string $sql The native SQL query string. + * @param Query\ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. + * + * @return void + */ + public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm) + { + $this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm); + } + + /** + * Gets the components of a previously registered named native query. + * + * @param string $name The name of the query. + * + * @return array A tuple with the first element being the SQL string and the second + * element being the ResultSetMapping. + * + * @throws ORMException + */ + public function getNamedNativeQuery($name) + { + if ( ! isset($this->_attributes['namedNativeQueries'][$name])) { + throw ORMException::namedNativeQueryNotFound($name); + } + + return $this->_attributes['namedNativeQueries'][$name]; + } + + /** + * Ensures that this Configuration instance contains settings that are + * suitable for a production environment. + * + * @return void + * + * @throws ORMException If a configuration setting has a value that is not + * suitable for a production environment. + */ + public function ensureProductionSettings() + { + if ( ! $this->getQueryCacheImpl()) { + throw ORMException::queryCacheNotConfigured(); + } + + if ( ! $this->getMetadataCacheImpl()) { + throw ORMException::metadataCacheNotConfigured(); + } + + if ($this->getAutoGenerateProxyClasses()) { + throw ORMException::proxyClassesAlwaysRegenerating(); + } + } + + /** + * Registers a custom DQL function that produces a string value. + * Such a function can then be used in any DQL statement in any place where string + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws ORMException + */ + public function addCustomStringFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customStringFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom string DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomStringFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customStringFunctions'][$name]) + ? $this->_attributes['customStringFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL string functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added string functions are discarded. + * + * @param array $functions The map of custom DQL string functions. + * + * @return void + */ + public function setCustomStringFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomStringFunction($name, $className); + } + } + + /** + * Registers a custom DQL function that produces a numeric value. + * Such a function can then be used in any DQL statement in any place where numeric + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws ORMException + */ + public function addCustomNumericFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customNumericFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom numeric DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomNumericFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customNumericFunctions'][$name]) + ? $this->_attributes['customNumericFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL numeric functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added numeric functions are discarded. + * + * @param array $functions The map of custom DQL numeric functions. + * + * @return void + */ + public function setCustomNumericFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomNumericFunction($name, $className); + } + } + + /** + * Registers a custom DQL function that produces a date/time value. + * Such a function can then be used in any DQL statement in any place where date/time + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws ORMException + */ + public function addCustomDatetimeFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom date/time DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomDatetimeFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customDatetimeFunctions'][$name]) + ? $this->_attributes['customDatetimeFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL date/time functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added date/time functions are discarded. + * + * @param array $functions The map of custom DQL date/time functions. + * + * @return void + */ + public function setCustomDatetimeFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomDatetimeFunction($name, $className); + } + } + + /** + * Sets the custom hydrator modes in one pass. + * + * @param array $modes An array of ($modeName => $hydrator). + * + * @return void + */ + public function setCustomHydrationModes($modes) + { + $this->_attributes['customHydrationModes'] = array(); + + foreach ($modes as $modeName => $hydrator) { + $this->addCustomHydrationMode($modeName, $hydrator); + } + } + + /** + * Gets the hydrator class for the given hydration mode name. + * + * @param string $modeName The hydration mode name. + * + * @return string|null The hydrator class name. + */ + public function getCustomHydrationMode($modeName) + { + return isset($this->_attributes['customHydrationModes'][$modeName]) + ? $this->_attributes['customHydrationModes'][$modeName] + : null; + } + + /** + * Adds a custom hydration mode. + * + * @param string $modeName The hydration mode name. + * @param string $hydrator The hydrator class name. + * + * @return void + */ + public function addCustomHydrationMode($modeName, $hydrator) + { + $this->_attributes['customHydrationModes'][$modeName] = $hydrator; + } + + /** + * Sets a class metadata factory. + * + * @param string $cmfName + * + * @return void + */ + public function setClassMetadataFactoryName($cmfName) + { + $this->_attributes['classMetadataFactoryName'] = $cmfName; + } + + /** + * @return string + */ + public function getClassMetadataFactoryName() + { + if ( ! isset($this->_attributes['classMetadataFactoryName'])) { + $this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory'; + } + + return $this->_attributes['classMetadataFactoryName']; + } + + /** + * Adds a filter to the list of possible filters. + * + * @param string $name The name of the filter. + * @param string $className The class name of the filter. + */ + public function addFilter($name, $className) + { + $this->_attributes['filters'][$name] = $className; + } + + /** + * Gets the class name for a given filter name. + * + * @param string $name The name of the filter. + * + * @return string The class name of the filter, or null of it is not + * defined. + */ + public function getFilterClassName($name) + { + return isset($this->_attributes['filters'][$name]) + ? $this->_attributes['filters'][$name] + : null; + } + + /** + * Sets default repository class. + * + * @since 2.2 + * + * @param string $className + * + * @return void + * + * @throws ORMException If not is a \Doctrine\Common\Persistence\ObjectRepository + */ + public function setDefaultRepositoryClassName($className) + { + $reflectionClass = new \ReflectionClass($className); + + if ( ! $reflectionClass->implementsInterface('Doctrine\Common\Persistence\ObjectRepository')) { + throw ORMException::invalidEntityRepository($className); + } + + $this->_attributes['defaultRepositoryClassName'] = $className; + } + + /** + * Get default repository class. + * + * @since 2.2 + * + * @return string + */ + public function getDefaultRepositoryClassName() + { + return isset($this->_attributes['defaultRepositoryClassName']) + ? $this->_attributes['defaultRepositoryClassName'] + : 'Doctrine\ORM\EntityRepository'; + } + + /** + * Sets naming strategy. + * + * @since 2.3 + * + * @param NamingStrategy $namingStrategy + * + * @return void + */ + public function setNamingStrategy(NamingStrategy $namingStrategy) + { + $this->_attributes['namingStrategy'] = $namingStrategy; + } + + /** + * Gets naming strategy.. + * + * @since 2.3 + * + * @return NamingStrategy + */ + public function getNamingStrategy() + { + if ( ! isset($this->_attributes['namingStrategy'])) { + $this->_attributes['namingStrategy'] = new DefaultNamingStrategy(); + } + + return $this->_attributes['namingStrategy']; + } + + /** + * Sets quote strategy. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\QuoteStrategy $quoteStrategy + * + * @return void + */ + public function setQuoteStrategy(QuoteStrategy $quoteStrategy) + { + $this->_attributes['quoteStrategy'] = $quoteStrategy; + } + + /** + * Gets quote strategy. + * + * @since 2.3 + * + * @return \Doctrine\ORM\Mapping\QuoteStrategy + */ + public function getQuoteStrategy() + { + if ( ! isset($this->_attributes['quoteStrategy'])) { + $this->_attributes['quoteStrategy'] = new DefaultQuoteStrategy(); + } + + return $this->_attributes['quoteStrategy']; + } + + /** + * Set the entity listener resolver. + * + * @since 2.4 + * @param \Doctrine\ORM\Mapping\EntityListenerResolver $resolver + */ + public function setEntityListenerResolver(EntityListenerResolver $resolver) + { + $this->_attributes['entityListenerResolver'] = $resolver; + } + + /** + * Get the entity listener resolver. + * + * @since 2.4 + * @return \Doctrine\ORM\Mapping\EntityListenerResolver + */ + public function getEntityListenerResolver() + { + if ( ! isset($this->_attributes['entityListenerResolver'])) { + $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver(); + } + + return $this->_attributes['entityListenerResolver']; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php new file mode 100644 index 00000000..dc123118 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -0,0 +1,271 @@ +. + */ + +namespace Doctrine\ORM\Decorator; + +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Common\Persistence\ObjectManagerDecorator; + +/** + * Base class for EntityManager decorators + * + * @since 2.4 + * @author Lars Strojny wrapped = $wrapped; + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->wrapped->getConnection(); + } + + /** + * {@inheritdoc} + */ + public function getExpressionBuilder() + { + return $this->wrapped->getExpressionBuilder(); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + return $this->wrapped->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function transactional($func) + { + return $this->wrapped->transactional($func); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->wrapped->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + return $this->wrapped->rollback(); + } + + /** + * {@inheritdoc} + */ + public function createQuery($dql = '') + { + return $this->wrapped->createQuery($dql); + } + + /** + * {@inheritdoc} + */ + public function createNamedQuery($name) + { + return $this->wrapped->createNamedQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + return $this->wrapped->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritdoc} + */ + public function createNamedNativeQuery($name) + { + return $this->wrapped->createNamedNativeQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createQueryBuilder() + { + return $this->wrapped->createQueryBuilder(); + } + + /** + * {@inheritdoc} + */ + public function getReference($entityName, $id) + { + return $this->wrapped->getReference($entityName, $id); + } + + /** + * {@inheritdoc} + */ + public function getPartialReference($entityName, $identifier) + { + return $this->wrapped->getPartialReference($entityName, $identifier); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->wrapped->close(); + } + + /** + * {@inheritdoc} + */ + public function copy($entity, $deep = false) + { + return $this->wrapped->copy($entity, $deep); + } + + /** + * {@inheritdoc} + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + return $this->wrapped->lock($entity, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null) + { + return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function flush($entity = null) + { + return $this->wrapped->flush($entity); + } + + /** + * {@inheritdoc} + */ + public function getEventManager() + { + return $this->wrapped->getEventManager(); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() + { + return $this->wrapped->getConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function isOpen() + { + return $this->wrapped->isOpen(); + } + + /** + * {@inheritdoc} + */ + public function getUnitOfWork() + { + return $this->wrapped->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function getHydrator($hydrationMode) + { + return $this->wrapped->getHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function newHydrator($hydrationMode) + { + return $this->wrapped->newHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function getProxyFactory() + { + return $this->wrapped->getProxyFactory(); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return $this->wrapped->getFilters(); + } + + /** + * {@inheritdoc} + */ + public function isFiltersStateClean() + { + return $this->wrapped->isFiltersStateClean(); + } + + /** + * {@inheritdoc} + */ + public function hasFilters() + { + return $this->wrapped->hasFilters(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php new file mode 100644 index 00000000..bc0d4ef1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php @@ -0,0 +1,1003 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; +use Doctrine\Common\EventManager; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Proxy\ProxyFactory; +use Doctrine\ORM\Query\FilterCollection; +use Doctrine\Common\Util\ClassUtils; + +/** + * The EntityManager is the central access point to ORM functionality. + * + * It is a facade to all different ORM subsystems such as UnitOfWork, + * Query Language and Repository API. Instantiation is done through + * the static create() method. The quickest way to obtain a fully + * configured EntityManager is: + * + * use Doctrine\ORM\Tools\Setup; + * use Doctrine\ORM\EntityManager; + * + * $paths = array('/path/to/entity/mapping/files'); + * + * $config = Setup::createAnnotationMetadataConfiguration($paths); + * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); + * $entityManager = EntityManager::create($dbParams, $config); + * + * For more information see + * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html} + * + * You should never attempt to inherit from the EntityManager: Inheritance + * is not a valid extension point for the EntityManager. Instead you + * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator} + * and wrap your entity manager in a decorator. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +/* final */class EntityManager implements EntityManagerInterface +{ + /** + * The used Configuration. + * + * @var \Doctrine\ORM\Configuration + */ + private $config; + + /** + * The database connection used by the EntityManager. + * + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + * + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * The EntityRepository instances. + * + * @var array + */ + private $repositories = array(); + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var \Doctrine\ORM\UnitOfWork + */ + private $unitOfWork; + + /** + * The event manager that is the central point of the event system. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * The maintained (cached) hydrators. One instance per type. + * + * @var array + */ + private $hydrators = array(); + + /** + * The proxy factory used to create dynamic proxies. + * + * @var \Doctrine\ORM\Proxy\ProxyFactory + */ + private $proxyFactory; + + /** + * The expression builder instance used to generate query expressions. + * + * @var \Doctrine\ORM\Query\Expr + */ + private $expressionBuilder; + + /** + * Whether the EntityManager is closed or not. + * + * @var bool + */ + private $closed = false; + + /** + * Collection of query filters. + * + * @var \Doctrine\ORM\Query\FilterCollection + */ + private $filterCollection; + + /** + * Creates a new EntityManager that operates on the given database connection + * and uses the given Configuration and EventManager implementations. + * + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\ORM\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + */ + protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager) + { + $this->conn = $conn; + $this->config = $config; + $this->eventManager = $eventManager; + + $metadataFactoryClassName = $config->getClassMetadataFactoryName(); + + $this->metadataFactory = new $metadataFactoryClassName; + $this->metadataFactory->setEntityManager($this); + $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); + + $this->unitOfWork = new UnitOfWork($this); + $this->proxyFactory = new ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses() + ); + } + + /** + * Gets the database connection object used by the EntityManager. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory() + { + return $this->metadataFactory; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * + * Example: + * + * + * $qb = $em->createQueryBuilder(); + * $expr = $em->getExpressionBuilder(); + * $qb->select('u')->from('User', 'u') + * ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2))); + * + * + * @return \Doctrine\ORM\Query\Expr + */ + public function getExpressionBuilder() + { + if ($this->expressionBuilder === null) { + $this->expressionBuilder = new Query\Expr; + } + + return $this->expressionBuilder; + } + + /** + * Starts a transaction on the underlying database connection. + * + * @return void + */ + public function beginTransaction() + { + $this->conn->beginTransaction(); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this EntityManager instance as an (optional) parameter. + * + * {@link flush} is invoked prior to transaction commit. + * + * If an exception occurs during execution of the function or flushing or transaction commit, + * the transaction is rolled back, the EntityManager closed and the exception re-thrown. + * + * @param callable $func The function to execute transactionally. + * + * @return mixed The non-empty value returned from the closure or true instead. + */ + public function transactional($func) + { + if (!is_callable($func)) { + throw new \InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"'); + } + + $this->conn->beginTransaction(); + + try { + $return = call_user_func($func, $this); + + $this->flush(); + $this->conn->commit(); + + return $return ?: true; + } catch (Exception $e) { + $this->close(); + $this->conn->rollback(); + + throw $e; + } + } + + /** + * Commits a transaction on the underlying database connection. + * + * @return void + */ + public function commit() + { + $this->conn->commit(); + } + + /** + * Performs a rollback on the underlying database connection. + * + * @return void + */ + public function rollback() + { + $this->conn->rollback(); + } + + /** + * Returns the ORM metadata 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)) or an aliased class name. + * + * Examples: + * MyProject\Domain\User + * sales:PriceRequest + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + * + * @internal Performance-sensitive method. + */ + public function getClassMetadata($className) + { + return $this->metadataFactory->getMetadataFor($className); + } + + /** + * Creates a new Query object. + * + * @param string $dql The DQL string. + * + * @return \Doctrine\ORM\Query + */ + public function createQuery($dql = '') + { + $query = new Query($this); + + if ( ! empty($dql)) { + $query->setDql($dql); + } + + return $query; + } + + /** + * Creates a Query from a named query. + * + * @param string $name + * + * @return \Doctrine\ORM\Query + */ + public function createNamedQuery($name) + { + return $this->createQuery($this->config->getNamedQuery($name)); + } + + /** + * Creates a native SQL query. + * + * @param string $sql + * @param ResultSetMapping $rsm The ResultSetMapping to use. + * + * @return NativeQuery + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + $query = new NativeQuery($this); + + $query->setSql($sql); + $query->setResultSetMapping($rsm); + + return $query; + } + + /** + * Creates a NativeQuery from a named native query. + * + * @param string $name + * + * @return \Doctrine\ORM\NativeQuery + */ + public function createNamedNativeQuery($name) + { + list($sql, $rsm) = $this->config->getNamedNativeQuery($name); + + return $this->createNativeQuery($sql, $rsm); + } + + /** + * Create a QueryBuilder instance + * + * @return QueryBuilder + */ + public function createQueryBuilder() + { + return new QueryBuilder($this); + } + + /** + * 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. + * + * If an entity is explicitly passed to this method only this entity and + * the cascade-persist semantics + scheduled inserts/removals are synchronized. + * + * @param null|object|array $entity + * + * @return void + * + * @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that + * makes use of optimistic locking fails. + */ + public function flush($entity = null) + { + $this->errorIfClosed(); + + $this->unitOfWork->commit($entity); + } + + /** + * Finds an Entity by its identifier. + * + * @param string $entityName + * @param mixed $id + * @param integer $lockMode + * @param integer|null $lockVersion + * + * @return object|null The entity instance or NULL if the entity can not be found. + * + * @throws OptimisticLockException + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws ORMException + */ + public function find($entityName, $id, $lockMode = LockMode::NONE, $lockVersion = null) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + if (is_object($id) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($id))) { + $id = $this->unitOfWork->getSingleIdentifierValue($id); + + if ($id === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + if ( ! is_array($id)) { + $id = array($class->identifier[0] => $id); + } + + $sortedId = array(); + + foreach ($class->identifier as $identifier) { + if ( ! isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + + $sortedId[$identifier] = $id[$identifier]; + } + + $unitOfWork = $this->getUnitOfWork(); + + // Check identity map first + if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) { + if ( ! ($entity instanceof $class->name)) { + return null; + } + + switch ($lockMode) { + case LockMode::OPTIMISTIC: + $this->lock($entity, $lockMode, $lockVersion); + break; + + case LockMode::PESSIMISTIC_READ: + case LockMode::PESSIMISTIC_WRITE: + $persister = $unitOfWork->getEntityPersister($class->name); + $persister->refresh($sortedId, $entity, $lockMode); + break; + } + + return $entity; // Hit! + } + + $persister = $unitOfWork->getEntityPersister($class->name); + + switch ($lockMode) { + case LockMode::NONE: + return $persister->load($sortedId); + + case LockMode::OPTIMISTIC: + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + $entity = $persister->load($sortedId); + + $unitOfWork->lock($entity, $lockMode, $lockVersion); + + return $entity; + + default: + if ( ! $this->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + return $persister->load($sortedId, null, null, array(), $lockMode); + } + } + + /** + * Gets a reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * @param string $entityName The name of the entity type. + * @param mixed $id The entity identifier. + * + * @return object The entity reference. + * + * @throws ORMException + */ + public function getReference($entityName, $id) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + if ( ! is_array($id)) { + $id = array($class->identifier[0] => $id); + } + + $sortedId = array(); + + foreach ($class->identifier as $identifier) { + if ( ! isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + + $sortedId[$identifier] = $id[$identifier]; + } + + // Check identity map first, if its already in there just return it. + if (($entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ($class->subClasses) { + return $this->find($entityName, $sortedId); + } + + if ( ! is_array($sortedId)) { + $sortedId = array($class->identifier[0] => $sortedId); + } + + $entity = $this->proxyFactory->getProxy($class->name, $sortedId); + + $this->unitOfWork->registerManaged($entity, $sortedId, array()); + + return $entity; + } + + /** + * Gets a partial reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * The returned reference may be a partial object if the entity is not yet loaded/managed. + * If it is a partial object it will not initialize the rest of the entity state on access. + * Thus you can only ever safely access the identifier of an entity obtained through + * this method. + * + * The use-cases for partial references involve maintaining bidirectional associations + * without loading one side of the association or to update an entity without loading it. + * Note, however, that in the latter case the original (persistent) entity data will + * never be visible to the application (especially not event listeners) as it will + * never be loaded in the first place. + * + * @param string $entityName The name of the entity type. + * @param mixed $identifier The entity identifier. + * + * @return object The (partial) entity reference. + */ + public function getPartialReference($entityName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + // Check identity map first, if its already in there just return it. + if (($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) !== false) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + + $entity = $class->newInstance(); + + $class->setIdentifierValues($entity, $identifier); + + $this->unitOfWork->registerManaged($entity, $identifier, array()); + $this->unitOfWork->markReadOnly($entity); + + return $entity; + } + + /** + * Clears the EntityManager. All entities that are currently managed + * by this EntityManager become detached. + * + * @param string|null $entityName if given, only entities of this type will get detached + * + * @return void + */ + public function clear($entityName = null) + { + $this->unitOfWork->clear($entityName); + } + + /** + * Closes the EntityManager. All entities that are currently managed + * by this EntityManager become detached. The EntityManager may no longer + * be used after it is closed. + * + * @return void + */ + public function close() + { + $this->clear(); + + $this->closed = true; + } + + /** + * Tells the EntityManager to make an instance managed and persistent. + * + * The entity will be entered into the database at or before transaction + * commit or as a result of the flush operation. + * + * NOTE: The persist operation always considers entities that are not yet known to + * this EntityManager as NEW. Do not pass detached entities to the persist operation. + * + * @param object $entity The instance to make managed and persistent. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function persist($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->persist($entity); + } + + /** + * Removes an entity instance. + * + * A removed entity will be removed from the database at or before transaction commit + * or as a result of the flush operation. + * + * @param object $entity The entity instance to remove. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function remove($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->remove($entity); + } + + /** + * Refreshes the persistent state of an entity from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $entity The entity to refresh. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function refresh($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->refresh($entity); + } + + /** + * Detaches an entity from the EntityManager, causing a managed entity to + * become detached. Unflushed changes made to the entity if any + * (including removal of the entity), will not be synchronized to the database. + * Entities which previously referenced the detached entity will continue to + * reference it. + * + * @param object $entity The entity to detach. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function detach($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()' , $entity); + } + + $this->unitOfWork->detach($entity); + } + + /** + * Merges the state of a detached entity into the persistence context + * of this EntityManager and returns the managed copy of the entity. + * The entity passed to merge will not become associated/managed with this EntityManager. + * + * @param object $entity The detached entity to merge into the persistence context. + * + * @return object The managed copy of the entity. + * + * @throws ORMInvalidArgumentException + */ + public function merge($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()' , $entity); + } + + $this->errorIfClosed(); + + return $this->unitOfWork->merge($entity); + } + + /** + * Creates a copy of the given entity. Can create a shallow or a deep copy. + * + * @param object $entity The entity to copy. + * @param boolean $deep FALSE for a shallow copy, TRUE for a deep copy. + * + * @return object The new entity. + * + * @throws \BadMethodCallException + * + * @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e: + * Fatal error: Maximum function nesting level of '100' reached, aborting! + */ + public function copy($entity, $deep = false) + { + throw new \BadMethodCallException("Not implemented."); + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int|null $lockVersion + * + * @return void + * + * @throws OptimisticLockException + * @throws PessimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + $this->unitOfWork->lock($entity, $lockMode, $lockVersion); + } + + /** + * Gets the repository for an entity class. + * + * @param string $entityName The name of the entity. + * + * @return EntityRepository The repository class. + */ + public function getRepository($entityName) + { + $entityName = ltrim($entityName, '\\'); + + if (isset($this->repositories[$entityName])) { + return $this->repositories[$entityName]; + } + + $metadata = $this->getClassMetadata($entityName); + $repositoryClassName = $metadata->customRepositoryClassName; + + if ($repositoryClassName === null) { + $repositoryClassName = $this->config->getDefaultRepositoryClassName(); + } + + $repository = new $repositoryClassName($this, $metadata); + + $this->repositories[$entityName] = $repository; + + return $repository; + } + + /** + * Determines whether an entity instance is managed in this EntityManager. + * + * @param object $entity + * + * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise. + */ + public function contains($entity) + { + return $this->unitOfWork->isScheduledForInsert($entity) + || $this->unitOfWork->isInIdentityMap($entity) + && ! $this->unitOfWork->isScheduledForDelete($entity); + } + + /** + * Gets the EventManager used by the EntityManager. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * Gets the Configuration used by the EntityManager. + * + * @return \Doctrine\ORM\Configuration + */ + public function getConfiguration() + { + return $this->config; + } + + /** + * Throws an exception if the EntityManager is closed or currently not active. + * + * @return void + * + * @throws ORMException If the EntityManager is closed. + */ + private function errorIfClosed() + { + if ($this->closed) { + throw ORMException::entityManagerClosed(); + } + } + + /** + * Check if the Entity manager is open or closed. + * + * @return bool + */ + public function isOpen() + { + return (!$this->closed); + } + + /** + * Gets the UnitOfWork used by the EntityManager to coordinate operations. + * + * @return \Doctrine\ORM\UnitOfWork + */ + public function getUnitOfWork() + { + return $this->unitOfWork; + } + + /** + * Gets a hydrator for the given hydration mode. + * + * This method caches the hydrator instances which is used for all queries that don't + * selectively iterate over the result. + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function getHydrator($hydrationMode) + { + if ( ! isset($this->hydrators[$hydrationMode])) { + $this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode); + } + + return $this->hydrators[$hydrationMode]; + } + + /** + * Create a new instance for the given hydration mode. + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + * + * @throws ORMException + */ + public function newHydrator($hydrationMode) + { + switch ($hydrationMode) { + case Query::HYDRATE_OBJECT: + return new Internal\Hydration\ObjectHydrator($this); + + case Query::HYDRATE_ARRAY: + return new Internal\Hydration\ArrayHydrator($this); + + case Query::HYDRATE_SCALAR: + return new Internal\Hydration\ScalarHydrator($this); + + case Query::HYDRATE_SINGLE_SCALAR: + return new Internal\Hydration\SingleScalarHydrator($this); + + case Query::HYDRATE_SIMPLEOBJECT: + return new Internal\Hydration\SimpleObjectHydrator($this); + + default: + if (($class = $this->config->getCustomHydrationMode($hydrationMode)) !== null) { + return new $class($this); + } + } + + throw ORMException::invalidHydrationMode($hydrationMode); + } + + /** + * Gets the proxy factory used by the EntityManager to create entity proxies. + * + * @return ProxyFactory + */ + public function getProxyFactory() + { + return $this->proxyFactory; + } + + /** + * 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) + { + $this->unitOfWork->initializeObject($obj); + } + + /** + * Factory method to create EntityManager instances. + * + * @param mixed $conn An array with the connection parameters or an existing Connection instance. + * @param Configuration $config The Configuration instance to use. + * @param EventManager $eventManager The EventManager instance to use. + * + * @return EntityManager The created EntityManager. + * + * @throws \InvalidArgumentException + * @throws ORMException + */ + public static function create($conn, Configuration $config, EventManager $eventManager = null) + { + if ( ! $config->getMetadataDriverImpl()) { + throw ORMException::missingMappingDriverImpl(); + } + + switch (true) { + case (is_array($conn)): + $conn = \Doctrine\DBAL\DriverManager::getConnection( + $conn, $config, ($eventManager ?: new EventManager()) + ); + break; + + case ($conn instanceof Connection): + if ($eventManager !== null && $conn->getEventManager() !== $eventManager) { + throw ORMException::mismatchedEventManager(); + } + break; + + default: + throw new \InvalidArgumentException("Invalid argument: " . $conn); + } + + return new EntityManager($conn, $config, $conn->getEventManager()); + } + + /** + * Gets the enabled filters. + * + * @return FilterCollection The active filter collection. + */ + public function getFilters() + { + if (null === $this->filterCollection) { + $this->filterCollection = new FilterCollection($this); + } + + return $this->filterCollection; + } + + /** + * Checks whether the state of the filter collection is clean. + * + * @return boolean True, if the filter collection is clean. + */ + public function isFiltersStateClean() + { + return null === $this->filterCollection || $this->filterCollection->isClean(); + } + + /** + * Checks whether the Entity Manager has filters. + * + * @return boolean True, if the EM has a filter collection. + */ + public function hasFilters() + { + return null !== $this->filterCollection; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php new file mode 100644 index 00000000..d72f7cd0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * EntityManager interface + * + * @since 2.4 + * @author Lars Strojny . + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when a Proxy fails to retrieve an Entity result. + * + * @author robo + * @since 2.0 + */ +class EntityNotFoundException extends ORMException +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct('Entity was not found.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php new file mode 100644 index 00000000..9a968d1b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Query\ResultSetMappingBuilder; + +use Doctrine\DBAL\LockMode; +use Doctrine\Common\Persistence\ObjectRepository; + +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * An EntityRepository serves as a repository for entities with generic as well as + * business specific methods for retrieving entities. + * + * This class is designed for inheritance and users can subclass this class to + * write their own repositories with business-specific methods to locate entities. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepository implements ObjectRepository, Selectable +{ + /** + * @var string + */ + protected $_entityName; + + /** + * @var EntityManager + */ + protected $_em; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $_class; + + /** + * Initializes a new EntityRepository. + * + * @param EntityManager $em The EntityManager to use. + * @param Mapping\ClassMetadata $class The class descriptor. + */ + public function __construct($em, Mapping\ClassMetadata $class) + { + $this->_entityName = $class->name; + $this->_em = $em; + $this->_class = $class; + } + + /** + * Creates a new QueryBuilder instance that is prepopulated for this entity name. + * + * @param string $alias + * + * @return QueryBuilder + */ + public function createQueryBuilder($alias) + { + return $this->_em->createQueryBuilder() + ->select($alias) + ->from($this->_entityName, $alias); + } + + /** + * Creates a new result set mapping builder for this entity. + * + * The column naming strategy is "INCREMENT". + * + * @param string $alias + * + * @return ResultSetMappingBuilder + */ + public function createResultSetMappingBuilder($alias) + { + $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); + $rsm->addRootEntityFromClassMetadata($this->_entityName, $alias); + + return $rsm; + } + + /** + * Creates a new Query instance based on a predefined metadata named query. + * + * @param string $queryName + * + * @return Query + */ + public function createNamedQuery($queryName) + { + return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); + } + + /** + * Creates a native SQL query. + * + * @param string $queryName + * + * @return NativeQuery + */ + public function createNativeNamedQuery($queryName) + { + $queryMapping = $this->_class->getNamedNativeQuery($queryName); + $rsm = new Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping); + + return $this->_em->createNativeQuery($queryMapping['query'], $rsm); + } + + /** + * Clears the repository, causing all managed entities to become detached. + * + * @return void + */ + public function clear() + { + $this->_em->clear($this->_class->rootEntityName); + } + + /** + * Finds an entity by its primary key / identifier. + * + * @param mixed $id The identifier. + * @param int $lockMode The lock mode. + * @param int|null $lockVersion The lock version. + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) + { + return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion); + } + + /** + * Finds all entities in the repository. + * + * @return array The entities. + */ + public function findAll() + { + return $this->findBy(array()); + } + + /** + * Finds entities by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array The objects. + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->loadAll($criteria, $orderBy, $limit, $offset); + } + + /** + * Finds a single entity by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function findOneBy(array $criteria, array $orderBy = null) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->load($criteria, null, null, array(), 0, 1, $orderBy); + } + + /** + * Adds support for magic finders. + * + * @param string $method + * @param array $arguments + * + * @return array|object The found entity/entities. + * + * @throws ORMException + * @throws \BadMethodCallException If the method called is an invalid find* method + * or no find* method at all and therefore an invalid + * method call. + */ + public function __call($method, $arguments) + { + switch (true) { + case (0 === strpos($method, 'findBy')): + $by = substr($method, 6); + $method = 'findBy'; + break; + + case (0 === strpos($method, 'findOneBy')): + $by = substr($method, 9); + $method = 'findOneBy'; + break; + + default: + throw new \BadMethodCallException( + "Undefined method '$method'. The method name must start with ". + "either findBy or findOneBy!" + ); + } + + if (empty($arguments)) { + throw ORMException::findByRequiresParameter($method . $by); + } + + $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); + + if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { + switch (count($arguments)) { + case 1: + return $this->$method(array($fieldName => $arguments[0])); + + case 2: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1]); + + case 3: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2]); + + case 4: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2], $arguments[3]); + + default: + // Do nothing + } + } + + throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); + } + + /** + * @return string + */ + protected function getEntityName() + { + return $this->_entityName; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->getEntityName(); + } + + /** + * @return EntityManager + */ + protected function getEntityManager() + { + return $this->_em; + } + + /** + * @return Mapping\ClassMetadata + */ + protected function getClassMetadata() + { + return $this->_class; + } + + /** + * Select all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return \Doctrine\Common\Collections\Collection + */ + public function matching(Criteria $criteria) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return new ArrayCollection($persister->loadCriteria($criteria)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php new file mode 100644 index 00000000..8977a357 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php @@ -0,0 +1,55 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManager; +use Doctrine\Common\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends BaseLifecycleEventArgs +{ + /** + * Retrieves associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->getObject(); + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php new file mode 100644 index 00000000..125855be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManager; +use Doctrine\Common\EventArgs; + +/** + * A method invoker based on entity lifecycle. + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ListenersInvoker +{ + const INVOKE_NONE = 0; + const INVOKE_LISTENERS = 1; + const INVOKE_CALLBACKS = 2; + const INVOKE_MANAGER = 4; + + /** + * @var \Doctrine\ORM\Mapping\EntityListenerResolver The Entity listener resolver. + */ + private $resolver; + + /** + * The EventManager used for dispatching events. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * Initializes a new ListenersInvoker instance. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->eventManager = $em->getEventManager(); + $this->resolver = $em->getConfiguration()->getEntityListenerResolver(); + } + + /** + * Get the subscribed event systems + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * + * @return integer Bitmask of subscribed event systems. + */ + public function getSubscribedSystems(ClassMetadata $metadata, $eventName) + { + $invoke = self::INVOKE_NONE; + + if (isset($metadata->lifecycleCallbacks[$eventName])) { + $invoke |= self::INVOKE_CALLBACKS; + } + + if (isset($metadata->entityListeners[$eventName])) { + $invoke |= self::INVOKE_LISTENERS; + } + + if ($this->eventManager->hasListeners($eventName)) { + $invoke |= self::INVOKE_MANAGER; + } + + return $invoke; + } + + /** + * Dispatches the lifecycle event of the given entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * @param object $entity The Entity on which the event occurred. + * @param \Doctrine\Common\EventArgs $event The Event args. + * @param integer $invoke Bitmask to invoke listeners. + */ + public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke) + { + if($invoke & self::INVOKE_CALLBACKS) { + foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) { + $entity->$callback($event); + } + } + + if($invoke & self::INVOKE_LISTENERS) { + foreach ($metadata->entityListeners[$eventName] as $listener) { + $class = $listener['class']; + $method = $listener['method']; + $instance = $this->resolver->resolve($class); + + $instance->$method($entity, $event); + } + } + + if($invoke & self::INVOKE_MANAGER) { + $this->eventManager->dispatchEvent($eventName, $event); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 00000000..0111fa38 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\EntityManager; +use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.0 + */ +class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs +{ + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php new file mode 100644 index 00000000..3a29680f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManager; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + * @param string|null $entityClass Optional entity class. + */ + public function __construct(EntityManager $em, $entityClass = null) + { + $this->em = $em; + $this->entityClass = $entityClass; + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Checks if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php new file mode 100644 index 00000000..2dd05a5f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManager; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php new file mode 100644 index 00000000..d1206008 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManager; +use Doctrine\Common\EventArgs; + +/** + * Provides event arguments for the postFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Daniel Freudenberger + */ +class PostFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php new file mode 100644 index 00000000..bfee5c76 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManager; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PreFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php new file mode 100644 index 00000000..0cae066a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php @@ -0,0 +1,138 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManager; + +/** + * Class that holds event arguments for a preInsert/preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param EntityManager $em + * @param array $changeSet + */ + public function __construct($entity, EntityManager $em, array &$changeSet) + { + parent::__construct($entity, $em); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieves entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + 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->getEntity()) + )); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php new file mode 100644 index 00000000..8c13fa2d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php @@ -0,0 +1,159 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Container for all ORM events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class is not meant to be instantiated. + */ + private function __construct() + { + } + + /** + * The preRemove event occurs for a given entity before the respective + * EntityManager remove operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preRemove = 'preRemove'; + + /** + * The postRemove event occurs for an entity after the entity has + * been deleted. It will be invoked after the database delete operations. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postRemove = 'postRemove'; + + /** + * The prePersist event occurs for a given entity before the respective + * EntityManager persist operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const prePersist = 'prePersist'; + + /** + * The postPersist event occurs for an entity after the entity has + * been made persistent. It will be invoked after the database insert operations. + * Generated primary key values are available in the postPersist event. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postPersist = 'postPersist'; + + /** + * The preUpdate event occurs before the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preUpdate = 'preUpdate'; + + /** + * The postUpdate event occurs after the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postUpdate = 'postUpdate'; + + /** + * The postLoad event occurs for an entity after the entity has been loaded + * into the current EntityManager from the database or after the refresh operation + * has been applied to it. + * + * Note that the postLoad event occurs for an entity before any associations have been + * initialized. Therefore it is not safe to access associations in a postLoad callback + * or event handler. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postLoad = 'postLoad'; + + /** + * The loadClassMetadata event occurs after the mapping metadata for a class + * has been loaded from a mapping source (annotations/xml/yaml). + * + * @var string + */ + const loadClassMetadata = 'loadClassMetadata'; + + /** + * The preFlush event occurs when the EntityManager#flush() operation is invoked, + * but before any changes to managed entities have been calculated. This event is + * always raised right after EntityManager#flush() call. + */ + const preFlush = 'preFlush'; + + /** + * The onFlush event occurs when the EntityManager#flush() operation is invoked, + * after any changes to managed entities have been determined but before any + * actual database operations are executed. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the onFlush event is not raised. + * + * @var string + */ + const onFlush = 'onFlush'; + + /** + * The postFlush event occurs when the EntityManager#flush() operation is invoked and + * after all actual database operations are executed successfully. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the postFlush event is not raised. The event won't be raised if an error occurs during the + * flush operation. + * + * @var string + */ + const postFlush = 'postFlush'; + + /** + * The onClear event occurs when the EntityManager#clear() operation is invoked, + * after all references to entities have been removed from the unit of work. + * + * @var string + */ + const onClear = 'onClear'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php new file mode 100644 index 00000000..b4161b29 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +abstract class AbstractIdGenerator +{ + /** + * Generates an identifier for an entity. + * + * @param \Doctrine\ORM\EntityManager $em + * @param \Doctrine\ORM\Mapping\Entity $entity + * + * @return mixed + */ + abstract public function generate(EntityManager $em, $entity); + + /** + * Gets whether this generator is a post-insert generator which means that + * {@link generate()} must be called after the entity has been inserted + * into the database. + * + * By default, this method returns FALSE. Generators that have this requirement + * must override this method and return TRUE. + * + * @return boolean + */ + public function isPostInsertGenerator() + { + return false; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php new file mode 100644 index 00000000..4e332159 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\ORMException; + +/** + * Special generator for application-assigned identifiers (doesn't really generate anything). + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AssignedGenerator extends AbstractIdGenerator +{ + /** + * Returns the identifier assigned to the given entity. + * + * @param EntityManager $em + * @param object $entity + * + * @return mixed + * + * @throws \Doctrine\ORM\ORMException + * + * @override + */ + public function generate(EntityManager $em, $entity) + { + $class = $em->getClassMetadata(get_class($entity)); + $idFields = $class->getIdentifierFieldNames(); + $identifier = array(); + + foreach ($idFields as $idField) { + $value = $class->getFieldValue($entity, $idField); + + if ( ! isset($value)) { + throw ORMException::entityMissingAssignedIdForField($entity, $idField); + } + + if (isset($class->associationMappings[$idField])) { + if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) { + throw ORMException::entityMissingForeignAssignedId($entity, $value); + } + + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. + $value = current($em->getUnitOfWork()->getEntityIdentifier($value)); + } + + $identifier[$idField] = $value; + } + + return $identifier; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php new file mode 100644 index 00000000..1b4ed9a0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that obtains IDs from special "identity" columns. These are columns + * that automatically get a database-generated, auto-incremented identifier on INSERT. + * This generator obtains the last insert id after such an insert. + */ +class BigIntegerIdentityGenerator extends AbstractIdGenerator +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. + */ + public function __construct($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function generate(EntityManager $em, $entity) + { + return (string)$em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritdoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php new file mode 100644 index 00000000..7945c693 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that obtains IDs from special "identity" columns. These are columns + * that automatically get a database-generated, auto-incremented identifier on INSERT. + * This generator obtains the last insert id after such an insert. + */ +class IdentityGenerator extends AbstractIdGenerator +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. + */ + public function __construct($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function generate(EntityManager $em, $entity) + { + return (int)$em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritdoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php new file mode 100644 index 00000000..54515225 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Serializable; +use Doctrine\ORM\EntityManager; + +/** + * Represents an ID generator that uses a database sequence. + * + * @since 2.0 + * @author Roman Borschel + */ +class SequenceGenerator extends AbstractIdGenerator implements Serializable +{ + /** + * The allocation size of the sequence. + * + * @var int + */ + private $_allocationSize; + + /** + * The name of the sequence. + * + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_nextValue = 0; + + /** + * @var int|null + */ + private $_maxValue = null; + + /** + * Initializes a new sequence generator. + * + * @param string $sequenceName The name of the sequence. + * @param integer $allocationSize The allocation size of the sequence. + */ + public function __construct($sequenceName, $allocationSize) + { + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * Generates an ID for the given entity. + * + * @param EntityManager $em + * @param object $entity + * + * @return integer The generated value. + * + * @override + */ + public function generate(EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + $sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName); + + $this->_nextValue = (int)$conn->fetchColumn($sql); + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + } + + return $this->_nextValue++; + } + + /** + * Gets the maximum value of the currently allocated bag of values. + * + * @return integer|null + */ + public function getCurrentMaxValue() + { + return $this->_maxValue; + } + + /** + * Gets the next value that will be returned by generate(). + * + * @return integer + */ + public function getNextValue() + { + return $this->_nextValue; + } + + /** + * @return string + */ + public function serialize() + { + return serialize(array( + 'allocationSize' => $this->_allocationSize, + 'sequenceName' => $this->_sequenceName + )); + } + + /** + * @param string $serialized + * + * @return void + */ + public function unserialize($serialized) + { + $array = unserialize($serialized); + + $this->_sequenceName = $array['sequenceName']; + $this->_allocationSize = $array['allocationSize']; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php new file mode 100644 index 00000000..34606fb2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that uses a single-row database table and a hi/lo algorithm. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class TableGenerator extends AbstractIdGenerator +{ + /** + * @var string + */ + private $_tableName; + + /** + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_allocationSize; + + /** + * @var int|null + */ + private $_nextValue; + + /** + * @var int|null + */ + private $_maxValue; + + /** + * @param string $tableName + * @param string $sequenceName + * @param int $allocationSize + */ + public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10) + { + $this->_tableName = $tableName; + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * {@inheritdoc} + */ + public function generate(EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + + if ($conn->getTransactionNestingLevel() === 0) { + // use select for update + $sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName); + $currentLevel = $conn->fetchColumn($sql); + + if ($currentLevel != null) { + $this->_nextValue = $currentLevel; + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + + $updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql( + $this->_tableName, $this->_sequenceName, $this->_allocationSize + ); + + if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) { + // no affected rows, concurrency issue, throw exception + } + } else { + // no current level returned, TableGenerator seems to be broken, throw exception + } + } else { + // only table locks help here, implement this or throw exception? + // or do we want to work with table locks exclusively? + } + } + + return $this->_nextValue++; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php new file mode 100644 index 00000000..901af806 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Represents an ID generator that uses the database UUID expression + * + * @since 2.3 + * @author Maarten de Keizer + */ +class UuidGenerator extends AbstractIdGenerator +{ + /** + * Generates an ID for the given entity. + * + * @param EntityManager $em The EntityManager to use. + * @param object $entity + * + * @return string The generated value. + * + * @override + */ + public function generate(EntityManager $em, $entity) + { + $conn = $em->getConnection(); + $sql = 'SELECT ' . $conn->getDatabasePlatform()->getGuidExpression(); + return $conn->query($sql)->fetchColumn(0); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php new file mode 100644 index 00000000..b5153e6d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\ORM\Internal; + +/** + * The CommitOrderCalculator is used by the UnitOfWork to sort out the + * correct order in which changes to entities need to be persisted. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class CommitOrderCalculator +{ + const NOT_VISITED = 1; + const IN_PROGRESS = 2; + const VISITED = 3; + + /** + * @var array + */ + private $_nodeStates = array(); + + /** + * The nodes to sort. + * + * @var array + */ + private $_classes = array(); + + /** + * @var array + */ + private $_relatedClasses = array(); + + /** + * @var array + */ + private $_sorted = array(); + + /** + * Clears the current graph. + * + * @return void + */ + public function clear() + { + $this->_classes = + $this->_relatedClasses = array(); + } + + /** + * Gets a valid commit order for all current nodes. + * + * Uses a depth-first search (DFS) to traverse the graph. + * The desired topological sorting is the reverse postorder of these searches. + * + * @return array The list of ordered classes. + */ + public function getCommitOrder() + { + // Check whether we need to do anything. 0 or 1 node is easy. + $nodeCount = count($this->_classes); + + if ($nodeCount <= 1) { + return ($nodeCount == 1) ? array_values($this->_classes) : array(); + } + + // Init + foreach ($this->_classes as $node) { + $this->_nodeStates[$node->name] = self::NOT_VISITED; + } + + // Go + foreach ($this->_classes as $node) { + if ($this->_nodeStates[$node->name] == self::NOT_VISITED) { + $this->_visitNode($node); + } + } + + $sorted = array_reverse($this->_sorted); + + $this->_sorted = $this->_nodeStates = array(); + + return $sorted; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $node + * + * @return void + */ + private function _visitNode($node) + { + $this->_nodeStates[$node->name] = self::IN_PROGRESS; + + if (isset($this->_relatedClasses[$node->name])) { + foreach ($this->_relatedClasses[$node->name] as $relatedNode) { + if ($this->_nodeStates[$relatedNode->name] == self::NOT_VISITED) { + $this->_visitNode($relatedNode); + } + } + } + + $this->_nodeStates[$node->name] = self::VISITED; + $this->_sorted[] = $node; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $fromClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $toClass + * + * @return void + */ + public function addDependency($fromClass, $toClass) + { + $this->_relatedClasses[$fromClass->name][] = $toClass; + } + + /** + * @param string $className + * + * @return bool + */ + public function hasClass($className) + { + return isset($this->_classes[$className]); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + public function addClass($class) + { + $this->_classes[$class->name] = $class; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php new file mode 100644 index 00000000..afd4b41a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -0,0 +1,455 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Base class for all hydrators. A hydrator is a class that provides some form + * of transformation of an SQL result set into another structure. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Guilherme Blanco + */ +abstract class AbstractHydrator +{ + /** + * The ResultSetMapping. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_rsm; + + /** + * The EntityManager instance. + * + * @var EntityManager + */ + protected $_em; + + /** + * The dbms Platform instance. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The UnitOfWork of the associated EntityManager. + * + * @var \Doctrine\ORM\UnitOfWork + */ + protected $_uow; + + /** + * The cache used during row-by-row hydration. + * + * @var array + */ + protected $_cache = array(); + + /** + * The statement that provides the data to hydrate. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $_stmt; + + /** + * The query hints. + * + * @var array + */ + protected $_hints; + + /** + * Initializes a new instance of a class derived from AbstractHydrator. + * + * @param \Doctrine\ORM\EntityManager $em The EntityManager to use. + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->_platform = $em->getConnection()->getDatabasePlatform(); + $this->_uow = $em->getUnitOfWork(); + } + + /** + * Initiates a row-by-row hydration. + * + * @param object $stmt + * @param object $resultSetMapping + * @param array $hints + * + * @return IterableResult + */ + public function iterate($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $evm = $this->_em->getEventManager(); + $evm->addEventListener(array(Events::onClear), $this); + + $this->prepare(); + + return new IterableResult($this); + } + + /** + * Hydrates all rows returned by the passed statement instance at once. + * + * @param object $stmt + * @param object $resultSetMapping + * @param array $hints + * + * @return array + */ + public function hydrateAll($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $this->prepare(); + + $result = $this->hydrateAllData(); + + $this->cleanup(); + + return $result; + } + + /** + * Hydrates a single row returned by the current statement instance during + * row-by-row hydration with {@link iterate()}. + * + * @return mixed + */ + public function hydrateRow() + { + $row = $this->_stmt->fetch(PDO::FETCH_ASSOC); + + if ( ! $row) { + $this->cleanup(); + + return false; + } + + $result = array(); + + $this->hydrateRowData($row, $this->_cache, $result); + + return $result; + } + + /** + * Executes one-time preparation tasks, once each time hydration is started + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function prepare() + { + } + + /** + * Executes one-time cleanup tasks at the end of a hydration that was initiated + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function cleanup() + { + $this->_rsm = null; + + $this->_stmt->closeCursor(); + $this->_stmt = null; + } + + /** + * Hydrates a single row from the current statement instance. + * + * Template method. + * + * @param array $data The row data. + * @param array $cache The cache to use. + * @param array $result The result to fill. + * + * @return void + * + * @throws HydrationException + */ + protected function hydrateRowData(array $data, array &$cache, array &$result) + { + throw new HydrationException("hydrateRowData() not implemented by this hydrator."); + } + + /** + * Hydrates all rows from the current statement instance at once. + * + * @return array + */ + abstract protected function hydrateAllData(); + + /** + * Processes a row of the result set. + * + * Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY). + * Puts the elements of a result row into a new array, grouped by the dql alias + * they belong to. The column names in the result set are mapped to their + * field names during this procedure as well as any necessary conversions on + * the values applied. Scalar values are kept in a specific key 'scalars'. + * + * @param array $data SQL Result Row. + * @param array &$cache Cache for column to field result information. + * @param array &$id Dql-Alias => ID-Hash. + * @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value? + * + * @return array An array with all the fields (name => value) of the data row, + * grouped by their component alias. + */ + protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents) + { + $rowData = array(); + + foreach ($data as $key => $value) { + // Parse each column name only once. Cache the results. + if ( ! isset($cache[$key])) { + switch (true) { + // NOTE: Most of the times it's a field mapping, so keep it first!!! + case (isset($this->_rsm->fieldMappings[$key])): + $fieldName = $this->_rsm->fieldMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]); + + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); + $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + case (isset($this->_rsm->scalarMappings[$key])): + $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; + $cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]); + $cache[$key]['isScalar'] = true; + break; + + case (isset($this->_rsm->metaMappings[$key])): + // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). + $fieldName = $this->_rsm->metaMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]); + + $cache[$key]['isMetaColumn'] = true; + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]); + break; + + default: + // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 + // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. + continue 2; + } + + if (isset($this->_rsm->newObjectMappings[$key])) { + $mapping = $this->_rsm->newObjectMappings[$key]; + + $cache[$key]['isNewObjectParameter'] = true; + $cache[$key]['argIndex'] = $mapping['argIndex']; + $cache[$key]['objIndex'] = $mapping['objIndex']; + $cache[$key]['class'] = new \ReflectionClass($mapping['className']); + } + } + + if (isset($cache[$key]['isNewObjectParameter'])) { + $class = $cache[$key]['class']; + $argIndex = $cache[$key]['argIndex']; + $objIndex = $cache[$key]['objIndex']; + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + $rowData['newObjects'][$objIndex]['class'] = $class; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value; + } + + if (isset($cache[$key]['isScalar'])) { + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + $rowData['scalars'][$cache[$key]['fieldName']] = $value; + + continue; + } + + $dqlAlias = $cache[$key]['dqlAlias']; + + if ($cache[$key]['isIdentifier']) { + $id[$dqlAlias] .= '|' . $value; + } + + if (isset($cache[$key]['isMetaColumn'])) { + if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value !== null) { + $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value; + if ($cache[$key]['isIdentifier']) { + $nonemptyComponents[$dqlAlias] = true; + } + } + + continue; + } + + // in an inheritance hierarchy the same field could be defined several times. + // We overwrite this value so long we don't have a non-null value, that value we keep. + // Per definition it cannot be that a field is defined several times and has several values. + if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) { + continue; + } + + $rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) { + $nonemptyComponents[$dqlAlias] = true; + } + } + + return $rowData; + } + + /** + * Processes a row of the result set. + * + * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that + * simply converts column names to field names and properly converts the + * values according to their types. The resulting row has the same number + * of elements as before. + * + * @param array $data + * @param array $cache + * + * @return array The processed row. + */ + protected function gatherScalarRowData(&$data, &$cache) + { + $rowData = array(); + + foreach ($data as $key => $value) { + // Parse each column name only once. Cache the results. + if ( ! isset($cache[$key])) { + switch (true) { + // NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!! + case (isset($this->_rsm->scalarMappings[$key])): + $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; + $cache[$key]['isScalar'] = true; + break; + + case (isset($this->_rsm->fieldMappings[$key])): + $fieldName = $this->_rsm->fieldMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]); + + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + case (isset($this->_rsm->metaMappings[$key])): + // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). + $cache[$key]['isMetaColumn'] = true; + $cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key]; + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + default: + // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 + // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. + continue 2; + } + } + + $fieldName = $cache[$key]['fieldName']; + + switch (true) { + case (isset($cache[$key]['isScalar'])): + $rowData[$fieldName] = $value; + break; + + case (isset($cache[$key]['isMetaColumn'])): + $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value; + break; + + default: + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value; + } + } + + return $rowData; + } + + /** + * Register entity as managed in UnitOfWork. + * + * @param ClassMetadata $class + * @param object $entity + * @param array $data + * + * @return void + * + * @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow + */ + protected function registerManaged(ClassMetadata $class, $entity, array $data) + { + if ($class->isIdentifierComposite) { + $id = array(); + foreach ($class->identifier as $fieldName) { + if (isset($class->associationMappings[$fieldName])) { + $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]; + } else { + $id[$fieldName] = $data[$fieldName]; + } + } + } else { + if (isset($class->associationMappings[$class->identifier[0]])) { + $id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]); + } else { + $id = array($class->identifier[0] => $data[$class->identifier[0]]); + } + } + + $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); + } + + /** + * When executed in a hydrate() loop we have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + public function onClear($eventArgs) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php new file mode 100644 index 00000000..bb18f32c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -0,0 +1,319 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * The ArrayHydrator produces a nested array "graph" that is often (not always) + * interchangeable with the corresponding object graph for read-only access. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ArrayHydrator extends AbstractHydrator +{ + /** + * @var array + */ + private $_ce = array(); + + /** + * @var array + */ + private $_rootAliases = array(); + + /** + * @var bool + */ + private $_isSimpleQuery = false; + + /** + * @var array + */ + private $_identifierMap = array(); + + /** + * @var array + */ + private $_resultPointers = array(); + + /** + * @var array + */ + private $_idTemplate = array(); + + /** + * @var int + */ + private $_resultCounter = 0; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1; + $this->_identifierMap = array(); + $this->_resultPointers = array(); + $this->_idTemplate = array(); + $this->_resultCounter = 0; + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->_identifierMap[$dqlAlias] = array(); + $this->_resultPointers[$dqlAlias] = array(); + $this->_idTemplate[$dqlAlias] = ''; + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $cache, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $row, array &$cache, array &$result) + { + // 1) Initialize + $id = $this->_idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + + // Extract scalar values. They're appended at the end. + if (isset($rowData['scalars'])) { + $scalars = $rowData['scalars']; + + unset($rowData['scalars']); + + if (empty($rowData)) { + ++$this->_resultCounter; + } + } + + // 2) Now hydrate the data found in the current row. + foreach ($rowData as $dqlAlias => $data) { + $index = false; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parent = $this->_rsm->parentAliasMap[$dqlAlias]; + $path = $parent . '.' . $dqlAlias; + + // missing parent data, skipping as RIGHT JOIN hydration is not supported. + if ( ! isset($nonemptyComponents[$parent]) ) { + continue; + } + + // Get a reference to the right element in the result tree. + // This element will get the associated element attached. + if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { + $first = reset($this->_resultPointers); + // TODO: Exception if $key === null ? + $baseElement =& $this->_resultPointers[$parent][key($first)]; + } else if (isset($this->_resultPointers[$parent])) { + $baseElement =& $this->_resultPointers[$parent]; + } else { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + continue; + } + + $relationAlias = $this->_rsm->relationMap[$dqlAlias]; + $relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $oneToOne = false; + + if (isset($nonemptyComponents[$dqlAlias])) { + if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + + $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); + $index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + $element = $data; + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element; + } else { + $baseElement[$relationAlias][] = $element; + } + + end($baseElement[$relationAlias]); + + $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]); + } + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + } else { + $oneToOne = true; + + if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = null; + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = $data; + } + } + + $coll =& $baseElement[$relationAlias]; + + if ($coll !== null) { + $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); + } + + } else { + // It's a root result element + + $this->_rootAliases[$dqlAlias] = true; // Mark as root + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array($entityKey => null); + } else { + $result[] = null; + } + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + continue; + } + + // Check for an existing element + if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $rowData[$dqlAlias]; + if ($this->_rsm->isMixed) { + $element = array($entityKey => $element); + } + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $result[$resultKey] = $element; + } else { + $resultKey = $this->_resultCounter; + $result[] = $element; + ++$this->_resultCounter; + } + + $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + } else { + $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; + $resultKey = $index; + /*if ($this->_rsm->isMixed) { + $result[] =& $result[$index]; + ++$this->_resultCounter; + }*/ + } + $this->updateResultPointer($result, $index, $dqlAlias, false); + } + } + + // Append scalar values to mixed result sets + if (isset($scalars)) { + if ( ! isset($resultKey) ) { + // this only ever happens when no object is fetched (scalar result only) + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->_resultCounter - 1; + } + } + + foreach ($scalars as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + } + + /** + * Updates the result pointer for an Entity. The result pointers point to the + * last seen instance of each Entity type. This is used for graph construction. + * + * @param array $coll The element. + * @param boolean|integer $index Index of the element in the collection. + * @param string $dqlAlias + * @param boolean $oneToOne Whether it is a single-valued association or not. + * + * @return void + */ + private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne) + { + if ($coll === null) { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + + return; + } + + if ($index !== false) { + $this->_resultPointers[$dqlAlias] =& $coll[$index]; + + return; + } + + if ( ! $coll) { + return; + } + + if ($oneToOne) { + $this->_resultPointers[$dqlAlias] =& $coll; + + return; + } + + end($coll); + $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; + + return; + } + + /** + * Retrieve ClassMetadata associated to entity class name. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getClassMetadata($className) + { + if ( ! isset($this->_ce[$className])) { + $this->_ce[$className] = $this->_em->getClassMetadata($className); + } + + return $this->_ce[$className]; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php new file mode 100644 index 00000000..c1c13c1f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -0,0 +1,91 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +class HydrationException extends \Doctrine\ORM\ORMException +{ + /** + * @return HydrationException + */ + public static function nonUniqueResult() + { + return new self("The result returned by the query was not unique."); + } + + /** + * @param string $alias + * @param string $parentAlias + * + * @return HydrationException + */ + public static function parentObjectOfRelationNotFound($alias, $parentAlias) + { + return new self("The parent object of entity result with alias '$alias' was not found." + . " The parent alias is '$parentAlias'."); + } + + /** + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function emptyDiscriminatorValue($dqlAlias) + { + return new self("The DQL alias '" . $dqlAlias . "' contains an entity ". + "of an inheritance hierarchy with an empty discriminator value. This means " . + "that the database contains inconsistent data with an empty " . + "discriminator value in a table row." + ); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorMetaMappingColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The meta mapping for the discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php new file mode 100644 index 00000000..3bbf6724 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * Represents a result structure that can be iterated over, hydrating row-by-row + * during the iteration. An IterableResult is obtained by AbstractHydrator#iterate(). + * + * @author robo + * @since 2.0 + */ +class IterableResult implements \Iterator +{ + /** + * @var \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + private $_hydrator; + + /** + * @var boolean + */ + private $_rewinded = false; + + /** + * @var integer + */ + private $_key = -1; + + /** + * @var object|null + */ + private $_current = null; + + /** + * @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator + */ + public function __construct($hydrator) + { + $this->_hydrator = $hydrator; + } + + /** + * @return void + * + * @throws HydrationException + */ + public function rewind() + { + if ($this->_rewinded == true) { + throw new HydrationException("Can only iterate a Result once."); + } else { + $this->_current = $this->next(); + $this->_rewinded = true; + } + } + + /** + * Gets the next set of results. + * + * @return array + */ + public function next() + { + $this->_current = $this->_hydrator->hydrateRow(); + $this->_key++; + return $this->_current; + } + + /** + * @return mixed + */ + public function current() + { + return $this->_current; + } + + /** + * @return int + */ + public function key() + { + return $this->_key; + } + + /** + * @return bool + */ + public function valid() + { + return ($this->_current!=false); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php new file mode 100644 index 00000000..fafc497c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -0,0 +1,629 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\UnitOfWork; +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Proxy\Proxy; + +/** + * The ObjectHydrator constructs an object graph out of an SQL result set. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + * @author Fabio B. Silva + * + * @internal Highly performance-sensitive code. + */ +class ObjectHydrator extends AbstractHydrator +{ + /** + * Local ClassMetadata cache to avoid going to the EntityManager all the time. + * This local cache is maintained between hydration runs and not cleared. + * + * @var array + */ + private $ce = array(); + + /* The following parts are reinitialized on every hydration run. */ + + /** + * @var array + */ + private $identifierMap; + + /** + * @var array + */ + private $resultPointers; + + /** + * @var array + */ + private $idTemplate; + + /** + * @var integer + */ + private $resultCounter; + + /** + * @var array + */ + private $rootAliases = array(); + + /** + * @var array + */ + private $initializedCollections = array(); + + /** + * @var array + */ + private $existingCollections = array(); + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $this->identifierMap = + $this->resultPointers = + $this->idTemplate = array(); + + $this->resultCounter = 0; + + if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { + $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; + } + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->identifierMap[$dqlAlias] = array(); + $this->idTemplate[$dqlAlias] = ''; + + if ( ! isset($this->ce[$className])) { + $this->ce[$className] = $this->_em->getClassMetadata($className); + } + + // Remember which associations are "fetch joined", so that we know where to inject + // collection stubs or proxies and where not. + if ( ! isset($this->_rsm->relationMap[$dqlAlias])) { + continue; + } + + if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) { + throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]); + } + + $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; + $sourceClass = $this->getClassMetadata($sourceClassName); + $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; + + $this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true; + + if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { + continue; + } + + // Mark any non-collection opposite sides as fetched, too. + if ($assoc['mappedBy']) { + $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true; + + continue; + } + + // handle fetch-joined owning side bi-directional one-to-one associations + if ($assoc['inversedBy']) { + $class = $this->ce[$className]; + $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; + + if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true; + } + } + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true; + + parent::cleanup(); + + $this->identifierMap = + $this->initializedCollections = + $this->existingCollections = + $this->resultPointers = array(); + + if ($eagerLoad) { + $this->_em->getUnitOfWork()->triggerEagerLoads(); + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $cache, $result); + } + + // Take snapshots from all newly initialized collections + foreach ($this->initializedCollections as $coll) { + $coll->takeSnapshot(); + } + + return $result; + } + + /** + * Initializes a related collection. + * + * @param object $entity The entity to which the collection belongs. + * @param ClassMetadata $class + * @param string $fieldName The name of the field on the entity that holds the collection. + * @param string $parentDqlAlias Alias of the parent fetch joining this collection. + * + * @return \Doctrine\ORM\PersistentCollection + */ + private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) + { + $oid = spl_object_hash($entity); + $relation = $class->associationMappings[$fieldName]; + $value = $class->reflFields[$fieldName]->getValue($entity); + + if ($value === null) { + $value = new ArrayCollection; + } + + if ( ! $value instanceof PersistentCollection) { + $value = new PersistentCollection( + $this->_em, $this->ce[$relation['targetEntity']], $value + ); + $value->setOwner($entity, $relation); + + $class->reflFields[$fieldName]->setValue($entity, $value); + $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); + + $this->initializedCollections[$oid . $fieldName] = $value; + } else if ( + isset($this->_hints[Query::HINT_REFRESH]) || + isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && + ! $value->isInitialized() + ) { + // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! + $value->setDirty(false); + $value->setInitialized(true); + $value->unwrap()->clear(); + + $this->initializedCollections[$oid . $fieldName] = $value; + } else { + // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! + $this->existingCollections[$oid . $fieldName] = $value; + } + + return $value; + } + + /** + * Gets an entity instance. + * + * @param array $data The instance data. + * @param string $dqlAlias The DQL alias of the entity's class. + * + * @return object The entity. + * + * @throws HydrationException + */ + private function getEntity(array $data, $dqlAlias) + { + $className = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { + + if ( ! isset($this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]])) { + throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $this->_rsm->discriminatorColumns[$dqlAlias], $dqlAlias); + } + + $discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]]; + + if ( ! isset($data[$discrColumn])) { + throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias); + } + + if ($data[$discrColumn] === "") { + throw HydrationException::emptyDiscriminatorValue($dqlAlias); + } + + $className = $this->ce[$className]->discriminatorMap[$data[$discrColumn]]; + + unset($data[$discrColumn]); + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) { + $this->registerManaged($this->ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $this->_hints['fetchAlias'] = $dqlAlias; + + return $this->_uow->createEntity($className, $data, $this->_hints); + } + + /** + * @param string $className + * @param array $data + * + * @return mixed + */ + private function getEntityFromIdentityMap($className, array $data) + { + // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? + $class = $this->ce[$className]; + + /* @var $class ClassMetadata */ + if ($class->isIdentifierComposite) { + $idHash = ''; + foreach ($class->identifier as $fieldName) { + if (isset($class->associationMappings[$fieldName])) { + $idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' '; + } else { + $idHash .= $data[$fieldName] . ' '; + } + } + return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName); + } else if (isset($class->associationMappings[$class->identifier[0]])) { + return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName); + } else { + return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); + } + } + + /** + * Gets a ClassMetadata instance from the local cache. + * If the instance is not yet in the local cache, it is loaded into the + * local cache. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + */ + private function getClassMetadata($className) + { + if ( ! isset($this->ce[$className])) { + $this->ce[$className] = $this->_em->getClassMetadata($className); + } + + return $this->ce[$className]; + } + + /** + * Hydrates a single row in an SQL result set. + * + * @internal + * First, the data of the row is split into chunks where each chunk contains data + * that belongs to a particular component/class. Afterwards, all these chunks + * are processed, one after the other. For each chunk of class data only one of the + * following code paths is executed: + * + * Path A: The data chunk belongs to a joined/associated object and the association + * is collection-valued. + * Path B: The data chunk belongs to a joined/associated object and the association + * is single-valued. + * Path C: The data chunk belongs to a root result element/object that appears in the topmost + * level of the hydrated result. A typical example are the objects of the type + * specified by the FROM clause in a DQL query. + * + * @param array $row The data of the row to process. + * @param array $cache The cache to use. + * @param array $result The result array to fill. + * + * @return void + */ + protected function hydrateRowData(array $row, array &$cache, array &$result) + { + // Initialize + $id = $this->idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + // Split the row data into chunks of class data. + $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + + // Extract scalar values. They're appended at the end. + if (isset($rowData['scalars'])) { + $scalars = $rowData['scalars']; + + unset($rowData['scalars']); + + if (empty($rowData)) { + ++$this->resultCounter; + } + } + + // Extract "new" object constructor arguments. They're appended at the end. + if (isset($rowData['newObjects'])) { + $newObjects = $rowData['newObjects']; + + unset($rowData['newObjects']); + + if (empty($rowData)) { + ++$this->resultCounter; + } + } + + // Hydrate the data chunks + foreach ($rowData as $dqlAlias => $data) { + $entityName = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias]; + // we need the $path to save into the identifier map which entities were already + // seen for this parent-child relationship + $path = $parentAlias . '.' . $dqlAlias; + + // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs + if ( ! isset($nonemptyComponents[$parentAlias])) { + // TODO: Add special case code where we hydrate the right join objects into identity map at least + continue; + } + + // Get a reference to the parent object to which the joined element belongs. + if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) { + $first = reset($this->resultPointers); + $parentObject = $first[key($first)]; + } else if (isset($this->resultPointers[$parentAlias])) { + $parentObject = $this->resultPointers[$parentAlias]; + } else { + // Parent object of relation not found, so skip it. + continue; + } + + $parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]]; + $oid = spl_object_hash($parentObject); + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $parentClass->associationMappings[$relationField]; + $reflField = $parentClass->reflFields[$relationField]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $reflFieldValue = $reflField->getValue($parentObject); + // PATH A: Collection-valued association + if (isset($nonemptyComponents[$dqlAlias])) { + $collKey = $oid . $relationField; + if (isset($this->initializedCollections[$collKey])) { + $reflFieldValue = $this->initializedCollections[$collKey]; + } else if ( ! isset($this->existingCollections[$collKey])) { + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } + + $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); + $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + if (isset($this->existingCollections[$collKey])) { + // Collection exists, only look for the element in the identity map. + if ($element = $this->getEntityFromIdentityMap($entityName, $data)) { + $this->resultPointers[$dqlAlias] = $element; + } else { + unset($this->resultPointers[$dqlAlias]); + } + } else { + $element = $this->getEntity($data, $dqlAlias); + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $reflFieldValue->hydrateSet($indexValue, $element); + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; + } else { + $reflFieldValue->hydrateAdd($element); + $reflFieldValue->last(); + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); + } + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + } + } else { + // Update result pointer + $this->resultPointers[$dqlAlias] = $reflFieldValue[$index]; + } + } else if ( ! $reflFieldValue) { + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) { + $reflFieldValue->setInitialized(true); + } + + } else { + // PATH B: Single-valued association + $reflFieldValue = $reflField->getValue($parentObject); + if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { + // we only need to take action if this value is null, + // we refresh the entity or its an unitialized proxy. + if (isset($nonemptyComponents[$dqlAlias])) { + $element = $this->getEntity($data, $dqlAlias); + $reflField->setValue($parentObject, $element); + $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); + $targetClass = $this->ce[$relation['targetEntity']]; + + if ($relation['isOwningSide']) { + //TODO: Just check hints['fetched'] here? + // If there is an inverse mapping on the target class its bidirectional + if ($relation['inversedBy']) { + $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; + if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); + } + } else if ($parentClass === $targetClass && $relation['mappedBy']) { + // Special case: bi-directional self-referencing one-one on the same class + $targetClass->reflFields[$relationField]->setValue($element, $parentObject); + } + } else { + // For sure bidirectional, as there is no inverse side in unidirectional mappings + $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); + } + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + } else { + $this->_uow->setOriginalEntityProperty($oid, $relationField, null); + $reflField->setValue($parentObject, null); + } + // else leave $reflFieldValue null for single-valued associations + } else { + // Update result pointer + $this->resultPointers[$dqlAlias] = $reflFieldValue; + } + } + } else { + // PATH C: Its a root result element + $this->rootAliases[$dqlAlias] = true; // Mark as root alias + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array($entityKey => null); + } else { + $result[] = null; + } + $resultKey = $this->resultCounter; + ++$this->resultCounter; + continue; + } + + // check for existing result from the iterations before + if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $this->getEntity($rowData[$dqlAlias], $dqlAlias); + + if ($this->_rsm->isMixed) { + $element = array($entityKey => $element); + } + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateSet($resultKey, $element); + } + + $result[$resultKey] = $element; + } else { + $resultKey = $this->resultCounter; + ++$this->resultCounter; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateAdd($element); + } + + $result[] = $element; + } + + $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + + } else { + // Update result pointer + $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]]; + $this->resultPointers[$dqlAlias] = $result[$index]; + $resultKey = $index; + /*if ($this->_rsm->isMixed) { + $result[] = $result[$index]; + ++$this->_resultCounter; + }*/ + } + } + } + + // Append scalar values to mixed result sets + if (isset($scalars)) { + if ( ! isset($resultKey) ) { + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->resultCounter - 1; + } + } + + foreach ($scalars as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + + // Append new object to mixed result sets + if (isset($newObjects)) { + if ( ! isset($resultKey) ) { + $resultKey = $this->resultCounter - 1; + } + + $count = count($newObjects); + + foreach ($newObjects as $objIndex => $newObject) { + $class = $newObject['class']; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if ($count === 1) { + $result[$resultKey] = $obj; + + continue; + } + + $result[$resultKey][$objIndex] = $obj; + } + } + } + + /** + * When executed in a hydrate() loop we may have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + public function onClear($eventArgs) + { + parent::onClear($eventArgs); + + $aliases = array_keys($this->identifierMap); + $this->identifierMap = array(); + + foreach ($aliases as $alias) { + $this->identifierMap[$alias] = array(); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php new file mode 100644 index 00000000..23b0abe2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * Hydrator that produces flat, rectangular results of scalar data. + * The created result is almost the same as a regular SQL result set, except + * that column names are mapped to field names and data type conversions take place. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $cache, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $data, array &$cache, array &$result) + { + $result[] = $this->gatherScalarRowData($data, $cache); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php new file mode 100644 index 00000000..249b6046 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -0,0 +1,188 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; + +class SimpleObjectHydrator extends AbstractHydrator +{ + /** + * @var ClassMetadata + */ + private $class; + + /** + * @var array + */ + private $declaringClasses = array(); + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $cache, $result); + } + + $this->_em->getUnitOfWork()->triggerEagerLoads(); + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function prepare() + { + if (count($this->_rsm->aliasMap) !== 1) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result."); + } + + if ($this->_rsm->scalarMappings) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings."); + } + + $this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap)); + + // We only need to add declaring classes if we have inheritance. + if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) { + return; + } + + foreach ($this->_rsm->declaringClasses as $column => $class) { + $this->declaringClasses[$column] = $this->_em->getClassMetadata($class); + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $sqlResult, array &$cache, array &$result) + { + $entityName = $this->class->name; + $data = array(); + + // We need to find the correct entity class name if we have inheritance in resultset + if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { + $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']); + + if ( ! isset($sqlResult[$discrColumnName])) { + throw HydrationException::missingDiscriminatorColumn($entityName, $discrColumnName, key($this->_rsm->aliasMap)); + } + + if ($sqlResult[$discrColumnName] === '') { + throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap)); + } + + $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]]; + + unset($sqlResult[$discrColumnName]); + } + + foreach ($sqlResult as $column => $value) { + // Hydrate column information if not yet present + if ( ! isset($cache[$column])) { + if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) { + continue; + } + + $cache[$column] = $info; + } + + // Convert field to a valid PHP value + if (isset($cache[$column]['field'])) { + $type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']); + $value = $type->convertToPHPValue($value, $this->_platform); + } + + // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) + if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) { + $data[$cache[$column]['name']] = $value; + } + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) { + $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $uow = $this->_em->getUnitOfWork(); + $entity = $uow->createEntity($entityName, $data, $this->_hints); + + $result[] = $entity; + } + + /** + * Retrieve column information form ResultSetMapping. + * + * @param string $entityName + * @param string $column + * + * @return array + */ + protected function hydrateColumnInfo($entityName, $column) + { + switch (true) { + case (isset($this->_rsm->fieldMappings[$column])): + $class = isset($this->declaringClasses[$column]) + ? $this->declaringClasses[$column] + : $this->class; + + // If class is not part of the inheritance, ignore + if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) { + return null; + } + + return array( + 'class' => $class, + 'name' => $this->_rsm->fieldMappings[$column], + 'field' => true, + ); + + case (isset($this->_rsm->relationMap[$column])): + $class = isset($this->_rsm->relationMap[$column]) + ? $this->_rsm->relationMap[$column] + : $this->class; + + // If class is not self referencing, ignore + if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) { + return null; + } + + // TODO: Decide what to do with associations. It seems original code is incomplete. + // One solution is to load the association, but it might require extra efforts. + return array('name' => $column); + + case (isset($this->_rsm->metaMappings[$column])): + return array( + 'name' => $this->_rsm->metaMappings[$column] + ); + + default: + return null; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php new file mode 100644 index 00000000..b9472417 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\NoResultException; +use Doctrine\ORM\NonUniqueResultException; + +/** + * Hydrator that hydrates a single scalar value from the result set. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class SingleScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC); + $numRows = count($data); + + if ($numRows === 0) { + throw new NoResultException(); + } + + if ($numRows > 1 || count($data[key($data)]) > 1) { + throw new NonUniqueResultException(); + } + + $cache = array(); + $result = $this->gatherScalarRowData($data[key($data)], $cache); + + return array_shift($result); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php new file mode 100644 index 00000000..19374ff3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +interface Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php new file mode 100644 index 00000000..1a9a31f1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mapping of property for an entity relationship. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AssociationOverride implements Annotation +{ + /** + * The name of the relationship property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The join column that is being mapped to the persistent attribute. + * + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns; + + /** + * The join table that maps the relationship. + * + * @var \Doctrine\ORM\Mapping\JoinTable + */ + public $joinTable; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php new file mode 100644 index 00000000..217c9e45 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mappings of relationship properties. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AssociationOverrides implements Annotation +{ + /** + * Mapping overrides of relationship properties. + * + * @var array<\Doctrine\ORM\Mapping\AssociationOverride> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php new file mode 100644 index 00000000..f86d3a15 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AttributeOverride implements Annotation +{ + /** + * The name of the property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The column definition. + * + * @var \Doctrine\ORM\Mapping\Column + */ + public $column; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php new file mode 100644 index 00000000..63b2cc66 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AttributeOverrides implements Annotation +{ + /** + * One or more field or property mapping overrides. + * + * @var array<\Doctrine\ORM\Mapping\AttributeOverride> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php new file mode 100644 index 00000000..942a662e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php @@ -0,0 +1,207 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; + +class AssociationBuilder +{ + /** + * @var ClassMetadataBuilder + */ + protected $builder; + + /** + * @var array + */ + protected $mapping; + + /** + * @var array|null + */ + protected $joinColumns; + + /** + * @var int + */ + protected $type; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + * @param int $type + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping, $type) + { + $this->builder = $builder; + $this->mapping = $mapping; + $this->type = $type; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function mappedBy($fieldName) + { + $this->mapping['mappedBy'] = $fieldName; + return $this; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function inversedBy($fieldName) + { + $this->mapping['inversedBy'] = $fieldName; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeAll() + { + $this->mapping['cascade'] = array("ALL"); + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadePersist() + { + $this->mapping['cascade'][] = "persist"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRemove() + { + $this->mapping['cascade'][] = "remove"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeMerge() + { + $this->mapping['cascade'][] = "merge"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeDetach() + { + $this->mapping['cascade'][] = "detach"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRefresh() + { + $this->mapping['cascade'][] = "refresh"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchExtraLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchEager() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_LAZY; + return $this; + } + + /** + * Add Join Columns. + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string|null $onDelete + * @param string|null $columnDef + * + * @return AssociationBuilder + */ + public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->joinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * @return ClassMetadataBuilder + * + * @throws \InvalidArgumentException + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + if ($this->type == ClassMetadata::MANY_TO_ONE) { + $cm->mapManyToOne($mapping); + } else if ($this->type == ClassMetadata::ONE_TO_ONE) { + $cm->mapOneToOne($mapping); + } else { + throw new \InvalidArgumentException("Type should be a ToOne Association here"); + } + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php new file mode 100644 index 00000000..774c9e06 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -0,0 +1,493 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Builder Object for ClassMetadata + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +class ClassMetadataBuilder +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + private $cm; + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $cm + */ + public function __construct(ClassMetadataInfo $cm) + { + $this->cm = $cm; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->cm; + } + + /** + * Marks the class as mapped superclass. + * + * @return ClassMetadataBuilder + */ + public function setMappedSuperClass() + { + $this->cm->isMappedSuperclass = true; + + return $this; + } + + /** + * Sets custom Repository class name. + * + * @param string $repositoryClassName + * + * @return ClassMetadataBuilder + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->cm->setCustomRepositoryClass($repositoryClassName); + + return $this; + } + + /** + * Marks class read only. + * + * @return ClassMetadataBuilder + */ + public function setReadOnly() + { + $this->cm->markReadOnly(); + + return $this; + } + + /** + * Sets the table name. + * + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function setTable($name) + { + $this->cm->setPrimaryTable(array('name' => $name)); + + return $this; + } + + /** + * Adds Index. + * + * @param array $columns + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function addIndex(array $columns, $name) + { + if (!isset($this->cm->table['indexes'])) { + $this->cm->table['indexes'] = array(); + } + + $this->cm->table['indexes'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Adds Unique Constraint. + * + * @param array $columns + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function addUniqueConstraint(array $columns, $name) + { + if ( ! isset($this->cm->table['uniqueConstraints'])) { + $this->cm->table['uniqueConstraints'] = array(); + } + + $this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Adds named query. + * + * @param string $name + * @param string $dqlQuery + * + * @return ClassMetadataBuilder + */ + public function addNamedQuery($name, $dqlQuery) + { + $this->cm->addNamedQuery(array( + 'name' => $name, + 'query' => $dqlQuery, + )); + + return $this; + } + + /** + * Sets class as root of a joined table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setJoinedTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); + + return $this; + } + + /** + * Sets class as root of a single table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setSingleTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); + + return $this; + } + + /** + * Sets the discriminator column details. + * + * @param string $name + * @param string $type + * @param int $length + * + * @return ClassMetadataBuilder + */ + public function setDiscriminatorColumn($name, $type = 'string', $length = 255) + { + $this->cm->setDiscriminatorColumn(array( + 'name' => $name, + 'type' => $type, + 'length' => $length, + )); + + return $this; + } + + /** + * Adds a subclass to this inheritance hierarchy. + * + * @param string $name + * @param string $class + * + * @return ClassMetadataBuilder + */ + public function addDiscriminatorMapClass($name, $class) + { + $this->cm->addDiscriminatorMapClass($name, $class); + + return $this; + } + + /** + * Sets deferred explicit change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyDeferredExplicit() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); + + return $this; + } + + /** + * Sets notify change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyNotify() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); + + return $this; + } + + /** + * Adds lifecycle event. + * + * @param string $methodName + * @param string $event + * + * @return ClassMetadataBuilder + */ + public function addLifecycleEvent($methodName, $event) + { + $this->cm->addLifecycleCallback($methodName, $event); + + return $this; + } + + /** + * Adds Field. + * + * @param string $name + * @param string $type + * @param array $mapping + * + * @return ClassMetadataBuilder + */ + public function addField($name, $type, array $mapping = array()) + { + $mapping['fieldName'] = $name; + $mapping['type'] = $type; + + $this->cm->mapField($mapping); + + return $this; + } + + /** + * Creates a field builder. + * + * @param string $name + * @param string $type + * + * @return FieldBuilder + */ + public function createField($name, $type) + { + return new FieldBuilder( + $this, + array( + 'fieldName' => $name, + 'type' => $type + ) + ); + } + + /** + * Adds a simple many to one association, optionally with the inversed by field. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addManyToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Creates a ManyToOne Association Builder. + * + * Note: This method does not add the association, you have to call build() on the AssociationBuilder. + * + * @param string $name + * @param string $targetEntity + * + * @return AssociationBuilder + */ + public function createManyToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_ONE + ); + } + + /** + * Creates a OneToOne Association Builder. + * + * @param string $name + * @param string $targetEntity + * + * @return AssociationBuilder + */ + public function createOneToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_ONE + ); + } + + /** + * Adds simple inverse one-to-one association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addInverseOneToOne($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToOne($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Adds simple owning one-to-one association. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createOneToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Creates a ManyToMany Association Builder. + * + * @param string $name + * @param string $targetEntity + * + * @return ManyToManyAssociationBuilder + */ + public function createManyToMany($name, $targetEntity) + { + return new ManyToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_MANY + ); + } + + /** + * Adds a simple owning many to many association. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addOwningManyToMany($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToMany($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Adds a simple inverse many to many association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addInverseManyToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createManyToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Creates a one to many association builder. + * + * @param string $name + * @param string $targetEntity + * + * @return OneToManyAssociationBuilder + */ + public function createOneToMany($name, $targetEntity) + { + return new OneToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_MANY + ); + } + + /** + * Adds simple OneToMany association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addOneToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php new file mode 100644 index 00000000..d17abeac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Events; + +/** + * Builder for entity listeners. + * + * @since 2.4 + * @author Fabio B. Silva + */ +class EntityListenerBuilder +{ + /** + * @var array Hash-map to handle event names. + */ + static private $events = array( + Events::preRemove => true, + Events::postRemove => true, + Events::prePersist => true, + Events::postPersist => true, + Events::preUpdate => true, + Events::postUpdate => true, + Events::postLoad => true, + Events::preFlush => true + ); + + /** + * Lookup the entity class to find methods that match to event lifecycle names + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $className The listener class name. + * + * @throws \Doctrine\ORM\Mapping\MappingException When the listener class not found. + */ + static public function bindEntityListener(ClassMetadata $metadata, $className) + { + $class = $metadata->fullyQualifiedClassName($className); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $className); + } + + foreach (get_class_methods($class) as $method) { + if ( ! isset(self::$events[$method])) { + continue; + } + + $metadata->addEntityListener($method, $class, $method); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php new file mode 100644 index 00000000..a4eb8cce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php @@ -0,0 +1,232 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Field Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + */ +class FieldBuilder +{ + /** + * @var ClassMetadataBuilder + */ + private $builder; + + /** + * @var array + */ + private $mapping; + + /** + * @var bool + */ + private $version; + + /** + * @var string + */ + private $generatedValue; + + /** + * @var array + */ + private $sequenceDef; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping) + { + $this->builder = $builder; + $this->mapping = $mapping; + } + + /** + * Sets length. + * + * @param int $length + * + * @return FieldBuilder + */ + public function length($length) + { + $this->mapping['length'] = $length; + return $this; + } + + /** + * Sets nullable. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function nullable($flag = true) + { + $this->mapping['nullable'] = (bool)$flag; + return $this; + } + + /** + * Sets Unique. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function unique($flag = true) + { + $this->mapping['unique'] = (bool)$flag; + return $this; + } + + /** + * Sets column name. + * + * @param string $name + * + * @return FieldBuilder + */ + public function columnName($name) + { + $this->mapping['columnName'] = $name; + return $this; + } + + /** + * Sets Precision. + * + * @param int $p + * + * @return FieldBuilder + */ + public function precision($p) + { + $this->mapping['precision'] = $p; + return $this; + } + + /** + * Sets scale. + * + * @param int $s + * + * @return FieldBuilder + */ + public function scale($s) + { + $this->mapping['scale'] = $s; + return $this; + } + + /** + * Sets field as primary key. + * + * @return FieldBuilder + */ + public function isPrimaryKey() + { + $this->mapping['id'] = true; + return $this; + } + + /** + * @param string $strategy + * + * @return FieldBuilder + */ + public function generatedValue($strategy = 'AUTO') + { + $this->generatedValue = $strategy; + return $this; + } + + /** + * Sets field versioned. + * + * @return FieldBuilder + */ + public function isVersionField() + { + $this->version = true; + return $this; + } + + /** + * Sets Sequence Generator. + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * + * @return FieldBuilder + */ + public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1) + { + $this->sequenceDef = array( + 'sequenceName' => $sequenceName, + 'allocationSize' => $allocationSize, + 'initialValue' => $initialValue, + ); + return $this; + } + + /** + * Sets column definition. + * + * @param string $def + * + * @return FieldBuilder + */ + public function columnDefinition($def) + { + $this->mapping['columnDefinition'] = $def; + return $this; + } + + /** + * Finalizes this field and attach it to the ClassMetadata. + * + * Without this call a FieldBuilder has no effect on the ClassMetadata. + * + * @return ClassMetadataBuilder + */ + public function build() + { + $cm = $this->builder->getClassMetadata(); + if ($this->generatedValue) { + $cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue)); + } + if ($this->version) { + $cm->setVersionMapping($this->mapping); + } + $cm->mapField($this->mapping); + if ($this->sequenceDef) { + $cm->setSequenceGeneratorDefinition($this->sequenceDef); + } + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php new file mode 100644 index 00000000..0dc0af48 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * ManyToMany Association Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder +{ + /** + * @var string|null + */ + private $joinTableName; + + /** + * @var array + */ + private $inverseJoinColumns = array(); + + /** + * @param string $name + * + * @return ManyToManyAssociationBuilder + */ + public function setJoinTable($name) + { + $this->joinTableName = $name; + return $this; + } + + /** + * Adds Inverse Join Columns. + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string|null $onDelete + * @param string|null $columnDef + * + * @return ManyToManyAssociationBuilder + */ + public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->inverseJoinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + $mapping['joinTable'] = array(); + if ($this->joinColumns) { + $mapping['joinTable']['joinColumns'] = $this->joinColumns; + } + if ($this->inverseJoinColumns) { + $mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns; + } + if ($this->joinTableName) { + $mapping['joinTable']['name'] = $this->joinTableName; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapManyToMany($mapping); + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php new file mode 100644 index 00000000..d8f4c395 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * OneToMany Association Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OneToManyAssociationBuilder extends AssociationBuilder +{ + /** + * @param array $fieldNames + * + * @return OneToManyAssociationBuilder + */ + public function setOrderBy(array $fieldNames) + { + $this->mapping['orderBy'] = $fieldNames; + return $this; + } + + /** + * @param string $fieldName + * + * @return OneToManyAssociationBuilder + */ + public function setIndexBy($fieldName) + { + $this->mapping['indexBy'] = $fieldName; + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapOneToMany($mapping); + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php new file mode 100644 index 00000000..3657b764 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class ChangeTrackingPolicy implements Annotation +{ + /** + * The change tracking policy. + * + * @var string + * + * @Enum({"DEFERRED_IMPLICIT", "DEFERRED_EXPLICIT", "NOTIFY"}) + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php new file mode 100644 index 00000000..a57f1e15 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * {@inheritDoc} + * + * @todo remove or rename ClassMetadataInfo to ClassMetadata + */ +class ClassMetadata extends ClassMetadataInfo +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php new file mode 100644 index 00000000..9320d1b6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -0,0 +1,572 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use ReflectionException; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\EntityManager; +use Doctrine\DBAL\Platforms; +use Doctrine\ORM\Events; +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; +use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; +use Doctrine\ORM\Id\IdentityGenerator; +use Doctrine\ORM\Id\BigIntegerIdentityGenerator; +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping information of a class which describes how a class should be mapped + * to a relational database. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ClassMetadataFactory extends AbstractClassMetadataFactory +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $targetPlatform; + + /** + * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + private $driver; + + /** + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * @param EntityManager $em + */ + public function setEntityManager(EntityManager $em) + { + $this->em = $em; + } + + /** + * {@inheritDoc}. + */ + protected function initialize() + { + $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); + $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); + $this->evm = $this->em->getEventManager(); + $this->initialized = true; + } + + /** + * {@inheritDoc} + */ + protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) + { + /* @var $class ClassMetadata */ + /* @var $parent ClassMetadata */ + if ($parent) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorColumn($parent->discriminatorColumn); + $class->setIdGeneratorType($parent->generatorType); + $this->addInheritedFields($class, $parent); + $this->addInheritedRelations($class, $parent); + $class->setIdentifier($parent->identifier); + $class->setVersioned($parent->isVersioned); + $class->setVersionField($parent->versionField); + $class->setDiscriminatorMap($parent->discriminatorMap); + $class->setLifecycleCallbacks($parent->lifecycleCallbacks); + $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); + + if ( ! empty($parent->customGeneratorDefinition)) { + $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition); + } + + if ($parent->isMappedSuperclass) { + $class->setCustomRepositoryClass($parent->customRepositoryClassName); + } + } + + // Invoke driver + try { + $this->driver->loadMetadataForClass($class->getName(), $class); + } catch (ReflectionException $e) { + throw MappingException::reflectionFailure($class->getName(), $e); + } + + // If this class has a parent the id generator strategy is inherited. + // However this is only true if the hierarchy of parents contains the root entity, + // if it consists of mapped superclasses these don't necessarily include the id field. + if ($parent && $rootEntityFound) { + if ($parent->isIdGeneratorSequence()) { + $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); + } else if ($parent->isIdGeneratorTable()) { + $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition; + } + + if ($parent->generatorType) { + $class->setIdGeneratorType($parent->generatorType); + } + + if ($parent->idGenerator) { + $class->setIdGenerator($parent->idGenerator); + } + } else { + $this->completeIdGeneratorMapping($class); + } + + if ($parent && $parent->isInheritanceTypeSingleTable()) { + $class->setPrimaryTable($parent->table); + } + + if ($parent && $parent->containsForeignIdentifier) { + $class->containsForeignIdentifier = true; + } + + if ($parent && !empty($parent->namedQueries)) { + $this->addInheritedNamedQueries($class, $parent); + } + + if ($parent && !empty($parent->namedNativeQueries)) { + $this->addInheritedNamedNativeQueries($class, $parent); + } + + if ($parent && !empty($parent->sqlResultSetMappings)) { + $this->addInheritedSqlResultSetMappings($class, $parent); + } + + if ($parent && !empty($parent->entityListeners) && empty($class->entityListeners)) { + $class->entityListeners = $parent->entityListeners; + } + + $class->setParentClasses($nonSuperclassParents); + + if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) { + $this->addDefaultDiscriminatorMap($class); + } + + if ($this->evm->hasListeners(Events::loadClassMetadata)) { + $eventArgs = new LoadClassMetadataEventArgs($class, $this->em); + $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); + } + + $this->validateRuntimeMetadata($class, $parent); + } + + /** + * Validate runtime metadata is correctly defined. + * + * @param ClassMetadata $class + * @param ClassMetadataInterface|null $parent + * + * @return void + * + * @throws MappingException + */ + protected function validateRuntimeMetadata($class, $parent) + { + if ( ! $class->reflClass ) { + // only validate if there is a reflection class instance + return; + } + + $class->validateIdentifier(); + $class->validateAssociations(); + $class->validateLifecycleCallbacks($this->getReflectionService()); + + // verify inheritance + if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { + if ( ! $parent) { + if (count($class->discriminatorMap) == 0) { + throw MappingException::missingDiscriminatorMap($class->name); + } + if ( ! $class->discriminatorColumn) { + throw MappingException::missingDiscriminatorColumn($class->name); + } + } else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { + // enforce discriminator map for all entities of an inheritance hierarchy, otherwise problems will occur. + throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); + } + } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { + // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy + throw MappingException::noInheritanceOnMappedSuperClass($class->name); + } + } + + /** + * {@inheritDoc} + */ + protected function newClassMetadataInstance($className) + { + return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy()); + } + + /** + * Adds a default discriminator map if no one is given + * + * If an entity is of any inheritance type and does not contain a + * discriminator map, then the map is generated automatically. This process + * is expensive computation wise. + * + * The automatically generated discriminator map contains the lowercase short name of + * each class as key. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @throws MappingException + */ + private function addDefaultDiscriminatorMap(ClassMetadata $class) + { + $allClasses = $this->driver->getAllClassNames(); + $fqcn = $class->getName(); + $map = array($this->getShortName($class->name) => $fqcn); + + $duplicates = array(); + foreach ($allClasses as $subClassCandidate) { + if (is_subclass_of($subClassCandidate, $fqcn)) { + $shortName = $this->getShortName($subClassCandidate); + + if (isset($map[$shortName])) { + $duplicates[] = $shortName; + } + + $map[$shortName] = $subClassCandidate; + } + } + + if ($duplicates) { + throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map); + } + + $class->setDiscriminatorMap($map); + } + + /** + * Gets the lower-case short name of a class. + * + * @param string $className + * + * @return string + */ + private function getShortName($className) + { + if (strpos($className, "\\") === false) { + return strtolower($className); + } + + $parts = explode("\\", $className); + return strtolower(end($parts)); + } + + /** + * Adds inherited fields to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->fieldMappings as $mapping) { + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedFieldMapping($mapping); + } + foreach ($parentClass->reflFields as $name => $field) { + $subClass->reflFields[$name] = $field; + } + } + + /** + * Adds inherited association mappings to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + * + * @throws MappingException + */ + private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->associationMappings as $field => $mapping) { + if ($parentClass->isMappedSuperclass) { + if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) { + throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field); + } + $mapping['sourceEntity'] = $subClass->name; + } + + //$subclassMapping = $mapping; + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedAssociationMapping($mapping); + } + } + + /** + * Adds inherited named queries to the subclass mapping. + * + * @since 2.2 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedQueries as $name => $query) { + if ( ! isset ($subClass->namedQueries[$name])) { + $subClass->addNamedQuery(array( + 'name' => $query['name'], + 'query' => $query['query'] + )); + } + } + } + + /** + * Adds inherited named native queries to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedNativeQueries as $name => $query) { + if ( ! isset ($subClass->namedNativeQueries[$name])) { + $subClass->addNamedNativeQuery(array( + 'name' => $query['name'], + 'query' => $query['query'], + 'isSelfClass' => $query['isSelfClass'], + 'resultSetMapping' => $query['resultSetMapping'], + 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass'], + )); + } + } + } + + /** + * Adds inherited sql result set mappings to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { + if ( ! isset ($subClass->sqlResultSetMappings[$name])) { + $entities = array(); + foreach ($mapping['entities'] as $entity) { + $entities[] = array( + 'fields' => $entity['fields'], + 'isSelfClass' => $entity['isSelfClass'], + 'discriminatorColumn' => $entity['discriminatorColumn'], + 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'], + ); + } + + $subClass->addSqlResultSetMapping(array( + 'name' => $mapping['name'], + 'columns' => $mapping['columns'], + 'entities' => $entities, + )); + } + } + } + + /** + * Completes the ID generator mapping. If "auto" is specified we choose the generator + * most appropriate for the targeted database platform. + * + * @param ClassMetadataInfo $class + * + * @return void + * + * @throws ORMException + */ + private function completeIdGeneratorMapping(ClassMetadataInfo $class) + { + $idGenType = $class->generatorType; + if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { + if ($this->targetPlatform->prefersSequences()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); + } else if ($this->targetPlatform->prefersIdentityColumns()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); + } else { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE); + } + } + + // Create & assign an appropriate ID generator instance + switch ($class->generatorType) { + case ClassMetadata::GENERATOR_TYPE_IDENTITY: + // For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to + //
__seq in PostgreSQL for SERIAL columns. + // Not pretty but necessary and the simplest solution that currently works. + $sequenceName = null; + $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; + + if ($this->targetPlatform instanceof Platforms\PostgreSQLPlatform) { + $columnName = $class->getSingleIdentifierColumnName(); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + $sequenceName = $class->getTableName() . '_' . $columnName . '_seq'; + $definition = array( + 'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName) + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $sequenceName = $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform); + } + + $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint') + ? new BigIntegerIdentityGenerator($sequenceName) + : new IdentityGenerator($sequenceName); + + $class->setIdGenerator($generator); + + break; + + case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + // If there is no sequence definition yet, create a default definition + $definition = $class->sequenceGeneratorDefinition; + + if ( ! $definition) { + $fieldName = $class->getSingleIdentifierFieldName(); + $columnName = $class->getSingleIdentifierColumnName(); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + $sequenceName = $class->getTableName() . '_' . $columnName . '_seq'; + $definition = array( + 'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName), + 'allocationSize' => 1, + 'initialValue' => 1, + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $class->setSequenceGeneratorDefinition($definition); + } + + $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator( + $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform), + $definition['allocationSize'] + ); + $class->setIdGenerator($sequenceGenerator); + break; + + case ClassMetadata::GENERATOR_TYPE_NONE: + $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_UUID: + $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_TABLE: + throw new ORMException("TableGenerator not yet implemented."); + break; + + case ClassMetadata::GENERATOR_TYPE_CUSTOM: + $definition = $class->customGeneratorDefinition; + if ( ! class_exists($definition['class'])) { + throw new ORMException("Can't instantiate custom generator : " . + $definition['class']); + } + $class->setIdGenerator(new $definition['class']); + break; + + default: + throw new ORMException("Unknown generator type: " . $class->generatorType); + } + } + + /** + * {@inheritDoc} + */ + protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->wakeupReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->initializeReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) + { + return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + /** + * {@inheritDoc} + */ + protected function getDriver() + { + return $this->driver; + } + + /** + * {@inheritDoc} + */ + protected function isEntity(ClassMetadataInterface $class) + { + return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php new file mode 100644 index 00000000..cda11d44 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -0,0 +1,3029 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use BadMethodCallException; +use InvalidArgumentException; +use RuntimeException; +use Doctrine\DBAL\Types\Type; +use ReflectionClass; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\ClassLoader; +use Doctrine\Common\EventArgs; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and its associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @author Roman Borschel + * @author Jonathan H. Wage + * @since 2.0 + */ +class ClassMetadataInfo implements ClassMetadata +{ + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + const INHERITANCE_TYPE_NONE = 1; + + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ + const INHERITANCE_TYPE_JOINED = 2; + + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ + const INHERITANCE_TYPE_SINGLE_TABLE = 3; + + /** + * TABLE_PER_CLASS means the class will be persisted according to the rules + * of Concrete Table Inheritance. + */ + const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; + + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ + const GENERATOR_TYPE_AUTO = 1; + + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_SEQUENCE = 2; + + /** + * TABLE means a separate table is used for id generation. + * Offers full portability. + */ + const GENERATOR_TYPE_TABLE = 3; + + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_IDENTITY = 4; + + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural, manually assigned id. + */ + const GENERATOR_TYPE_NONE = 5; + + /** + * UUID means that a UUID/GUID expression is used for id generation. Full + * portability is currently not guaranteed. + */ + const GENERATOR_TYPE_UUID = 6; + + /** + * CUSTOM means that customer will use own ID generator that supposedly work + */ + const GENERATOR_TYPE_CUSTOM = 7; + + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through persist() or a cascade). + */ + const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + + /** + * NOTIFY means that Doctrine relies on the entities sending out notifications + * when their properties change. Such entity classes must implement + * the NotifyPropertyChanged interface. + */ + const CHANGETRACKING_NOTIFY = 3; + + /** + * Specifies that an association is to be fetched when it is first accessed. + */ + const FETCH_LAZY = 2; + + /** + * Specifies that an association is to be fetched when the owner of the + * association is fetched. + */ + const FETCH_EAGER = 3; + + /** + * Specifies that an association is to be fetched lazy (on first access) and that + * commands such as Collection#count, Collection#slice are issued directly against + * the database if the collection is not yet initialized. + */ + const FETCH_EXTRA_LAZY = 4; + + /** + * Identifies a one-to-one association. + */ + const ONE_TO_ONE = 1; + + /** + * Identifies a many-to-one association. + */ + const MANY_TO_ONE = 2; + + /** + * Identifies a one-to-many association. + */ + const ONE_TO_MANY = 4; + + /** + * Identifies a many-to-many association. + */ + const MANY_TO_MANY = 8; + + /** + * Combined bitmask for to-one (single-valued) associations. + */ + const TO_ONE = 3; + + /** + * Combined bitmask for to-many (collection-valued) associations. + */ + const TO_MANY = 12; + + /** + * READ-ONLY: The name of the entity class. + * + * @var string + */ + public $name; + + /** + * READ-ONLY: The namespace the entity class is contained in. + * + * @var string + * + * @todo Not really needed. Usage could be localized. + */ + public $namespace; + + /** + * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance + * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same + * as {@link $entityName}. + * + * @var string + */ + public $rootEntityName; + + /** + * READ-ONLY: The definition of custom generator. Only used for CUSTOM + * generator type + * + * The definition has the following structure: + * + * array( + * 'class' => 'ClassName', + * ) + * + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $customGeneratorDefinition; + + /** + * The name of the custom repository class used for the entity class. + * (Optional). + * + * @var string + */ + public $customRepositoryClassName; + + /** + * READ-ONLY: Whether this class describes the mapping of a mapped superclass. + * + * @var boolean + */ + public $isMappedSuperclass = false; + + /** + * READ-ONLY: The names of the parent classes (ancestors). + * + * @var array + */ + public $parentClasses = array(); + + /** + * READ-ONLY: The names of all subclasses (descendants). + * + * @var array + */ + public $subClasses = array(); + + /** + * READ-ONLY: The named queries allowed to be called directly from Repository. + * + * @var array + */ + public $namedQueries = array(); + + /** + * READ-ONLY: The named native queries allowed to be called directly from Repository. + * + * A native SQL named query definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'query'              => ,
+     *     'resultClass'        => ,
+     *     'resultSetMapping'   => 
+     * )
+     * 
+ * + * @var array + */ + public $namedNativeQueries = array(); + + /** + * READ-ONLY: The mappings of the results of native SQL queries. + * + * A native result mapping definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'entities'           => array(),
+     *     'columns'            => array()
+     * )
+     * 
+ * + * @var array + */ + public $sqlResultSetMappings = array(); + + /** + * READ-ONLY: The field names of all fields that are part of the identifier/primary key + * of the mapped entity class. + * + * @var array + */ + public $identifier = array(); + + /** + * READ-ONLY: The inheritance mapping type used by the class. + * + * @var integer + */ + public $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * READ-ONLY: The Id generator type used by the class. + * + * @var int + */ + public $generatorType = self::GENERATOR_TYPE_NONE; + + /** + * READ-ONLY: The field mappings of the class. + * Keys are field names and values are mapping definitions. + * + * The mapping definition array has the following values: + * + * - fieldName (string) + * The name of the field in the Entity. + * + * - type (string) + * The type name of the mapped field. Can be one of Doctrine's mapping types + * or a custom mapping type. + * + * - columnName (string, optional) + * The column name. Optional. Defaults to the field name. + * + * - length (integer, optional) + * The database length of the column. Optional. Default value taken from + * the type. + * + * - id (boolean, optional) + * Marks the field as the primary key of the entity. Multiple fields of an + * entity can have the id attribute, forming a composite key. + * + * - nullable (boolean, optional) + * Whether the column is nullable. Defaults to FALSE. + * + * - columnDefinition (string, optional, schema-only) + * The SQL fragment that is used when generating the DDL for the column. + * + * - precision (integer, optional, schema-only) + * The precision of a decimal column. Only valid if the column type is decimal. + * + * - scale (integer, optional, schema-only) + * The scale of a decimal column. Only valid if the column type is decimal. + * + [* - 'unique'] (string, optional, schema-only) + * Whether a unique constraint should be generated for the column. + * + * @var array + */ + public $fieldMappings = array(); + + /** + * READ-ONLY: An array of field names. Used to look up field names from column names. + * Keys are column names and values are field names. + * This is the reverse lookup map of $_columnNames. + * + * @var array + */ + public $fieldNames = array(); + + /** + * READ-ONLY: A map of field names to column names. Keys are field names and values column names. + * Used to look up column names from field names. + * This is the reverse lookup map of $_fieldNames. + * + * @var array + * + * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName']. + */ + public $columnNames = array(); + + /** + * READ-ONLY: The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * + * @see discriminatorColumn + */ + public $discriminatorValue; + + /** + * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * + * @see discriminatorColumn + */ + public $discriminatorMap = array(); + + /** + * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE + * inheritance mappings. + * + * @var array + */ + public $discriminatorColumn; + + /** + * READ-ONLY: The primary table definition. The definition is an array with the + * following entries: + * + * name => + * schema => + * indexes => array + * uniqueConstraints => array + * + * @var array + */ + public $table; + + /** + * READ-ONLY: The registered lifecycle callbacks for entities of this class. + * + * @var array + */ + public $lifecycleCallbacks = array(); + + /** + * READ-ONLY: The registered entity listeners. + * + * @var array + */ + public $entityListeners = array(); + + /** + * READ-ONLY: The association mappings of this class. + * + * The mapping definition array supports the following keys: + * + * - fieldName (string) + * The name of the field in the entity the association is mapped to. + * + * - targetEntity (string) + * The class name of the target entity. If it is fully-qualified it is used as is. + * If it is a simple, unqualified class name the namespace is assumed to be the same + * as the namespace of the source entity. + * + * - mappedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the owning side. + * This key must be specified on the inverse side of a bidirectional association. + * + * - inversedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the inverse side. + * This key must be specified on the owning side of a bidirectional association. + * + * - cascade (array, optional) + * The names of persistence operations to cascade on the association. The set of possible + * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others). + * + * - orderBy (array, one-to-many/many-to-many only) + * A map of field names (of the target entity) to sorting directions (ASC/DESC). + * Example: array('priority' => 'desc') + * + * - fetch (integer, optional) + * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. + * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY. + * + * - joinTable (array, optional, many-to-many only) + * Specification of the join table and its join columns (foreign keys). + * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped + * through a join table by simply mapping the association as many-to-many with a unique + * constraint on the join table. + * + * - indexBy (string, optional, to-many only) + * Specification of a field on target-entity that is used to index the collection by. + * This field HAS to be either the primary key or a unique column. Otherwise the collection + * does not contain all the entities that are actually related. + * + * A join table definition has the following structure: + *
+     * array(
+     *     'name' => ,
+     *      'joinColumns' => array(),
+     *      'inverseJoinColumns' => array()
+     * )
+     * 
+ * + * @var array + */ + public $associationMappings = array(); + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite. + * + * @var boolean + */ + public $isIdentifierComposite = false; + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association. + * + * This flag is necessary because some code blocks require special treatment of this cases. + * + * @var boolean + */ + public $containsForeignIdentifier = false; + + /** + * READ-ONLY: The ID generator used for generating IDs for this class. + * + * @var \Doctrine\ORM\Id\AbstractIdGenerator + * + * @todo Remove! + */ + public $idGenerator; + + /** + * READ-ONLY: The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $sequenceGeneratorDefinition; + + /** + * READ-ONLY: The definition of the table generator of this class. Only used for the + * TABLE generation strategy. + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $tableGeneratorDefinition; + + /** + * READ-ONLY: The policy used for change-tracking on entities of this class. + * + * @var integer + */ + public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to be versioned + * with optimistic locking. + * + * @var boolean + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed + */ + public $versionField; + + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass + */ + public $reflClass; + + /** + * Is this entity marked as "read-only"? + * + * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance + * optimization for entities that are immutable, either in your domain or through the relation database + * (coming from a view, or a history table for example). + * + * @var bool + */ + public $isReadOnly = false; + + /** + * NamingStrategy determining the default column and table names. + * + * @var \Doctrine\ORM\Mapping\NamingStrategy + */ + protected $namingStrategy; + + /** + * The ReflectionProperty instances of the mapped class. + * + * @var \ReflectionProperty[] + */ + public $reflFields = array(); + + /** + * The prototype from which new instances of the mapped class are created. + * + * @var object + */ + private $_prototype; + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $entityName The name of the entity class the new instance is used for. + * @param NamingStrategy|null $namingStrategy + */ + public function __construct($entityName, NamingStrategy $namingStrategy = null) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy(); + } + + /** + * Gets the ReflectionProperties of the mapped class. + * + * @return array An array of ReflectionProperty instances. + */ + public function getReflectionProperties() + { + return $this->reflFields; + } + + /** + * Gets a ReflectionProperty for a specific field of the mapped class. + * + * @param string $name + * + * @return \ReflectionProperty + */ + public function getReflectionProperty($name) + { + return $this->reflFields[$name]; + } + + /** + * Gets the ReflectionProperty for the single identifier field. + * + * @return \ReflectionProperty + * + * @throws BadMethodCallException If the class has a composite identifier. + */ + public function getSingleIdReflectionProperty() + { + if ($this->isIdentifierComposite) { + throw new BadMethodCallException("Class " . $this->name . " has a composite identifier."); + } + return $this->reflFields[$this->identifier[0]]; + } + + /** + * Extracts the identifier values of an entity of this class. + * + * For composite identifiers, the identifier values are returned as an array + * with the same order as the field order in {@link identifier}. + * + * @param object $entity + * + * @return array + */ + public function getIdentifierValues($entity) + { + if ($this->isIdentifierComposite) { + $id = array(); + + foreach ($this->identifier as $idField) { + $value = $this->reflFields[$idField]->getValue($entity); + + if ($value !== null) { + $id[$idField] = $value; + } + } + + return $id; + } + + $id = $this->identifier[0]; + $value = $this->reflFields[$id]->getValue($entity); + + if (null === $value) { + return array(); + } + + return array($id => $value); + } + + /** + * Populates the entity identifier of an entity. + * + * @param object $entity + * @param mixed $id + * + * @return void + * + * @todo Rename to assignIdentifier() + */ + public function setIdentifierValues($entity, array $id) + { + foreach ($id as $idField => $idValue) { + $this->reflFields[$idField]->setValue($entity, $idValue); + } + } + + /** + * Sets the specified field to the specified value on the given entity. + * + * @param object $entity + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setFieldValue($entity, $field, $value) + { + $this->reflFields[$field]->setValue($entity, $value); + } + + /** + * Gets the specified field's value off the given entity. + * + * @param object $entity + * @param string $field + * + * @return mixed + */ + public function getFieldValue($entity, $field) + { + return $this->reflFields[$field]->getValue($entity); + } + + /** + * Creates a string representation of this instance. + * + * @return string The string representation of this instance. + * + * @todo Construct meaningful string representation. + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * Parts that are also NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + // This metadata is always serialized/cached. + $serialized = array( + 'associationMappings', + 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName'] + 'fieldMappings', + 'fieldNames', + 'identifier', + 'isIdentifierComposite', // TODO: REMOVE + 'name', + 'namespace', // TODO: REMOVE + 'table', + 'rootEntityName', + 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. + ); + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorColumn'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->generatorType != self::GENERATOR_TYPE_NONE) { + $serialized[] = 'generatorType'; + if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) { + $serialized[] = 'sequenceGeneratorDefinition'; + } + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->entityListeners) { + $serialized[] = 'entityListeners'; + } + + if ($this->namedQueries) { + $serialized[] = 'namedQueries'; + } + + if ($this->namedNativeQueries) { + $serialized[] = 'namedNativeQueries'; + } + + if ($this->sqlResultSetMappings) { + $serialized[] = 'sqlResultSetMappings'; + } + + if ($this->isReadOnly) { + $serialized[] = 'isReadOnly'; + } + + if ($this->customGeneratorDefinition) { + $serialized[] = "customGeneratorDefinition"; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + if ($this->_prototype === null) { + $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); + } + + return clone $this->_prototype; + } + /** + * Restores some state that can not be serialized/unserialized. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + */ + public function wakeupReflection($reflService) + { + // Restore ReflectionClass and properties + $this->reflClass = $reflService->getClass($this->name); + + foreach ($this->fieldMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + + foreach ($this->associationMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + } + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service. + * + * @return void + */ + public function initializeReflection($reflService) + { + $this->reflClass = $reflService->getClass($this->name); + $this->namespace = $reflService->getClassNamespace($this->name); + + if ($this->reflClass) { + $this->name = $this->rootEntityName = $this->reflClass->getName(); + } + + $this->table['name'] = $this->namingStrategy->classToTableName($this->name); + } + + /** + * Validates Identifier. + * + * @return void + * + * @throws MappingException + */ + public function validateIdentifier() + { + // Verify & complete identifier mapping + if ( ! $this->identifier && ! $this->isMappedSuperclass) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validates association targets actually exist. + * + * @return void + * + * @throws MappingException + */ + public function validateAssociations() + { + foreach ($this->associationMappings as $mapping) { + if ( ! ClassLoader::classExists($mapping['targetEntity']) ) { + throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); + } + } + } + + /** + * Validates lifecycle callbacks. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + * + * @throws MappingException + */ + public function validateLifecycleCallbacks($reflService) + { + foreach ($this->lifecycleCallbacks as $callbacks) { + foreach ($callbacks as $callbackFuncName) { + if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { + throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return $this->reflClass; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + * + * @return void + */ + public function setChangeTrackingPolicy($policy) + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredExplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredImplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Whether the change tracking policy of this class is "notify". + * + * @return boolean + */ + public function isChangeTrackingNotify() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * FALSE otherwise. + */ + public function isIdentifier($fieldName) + { + if ( ! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + return in_array($fieldName, $this->identifier); + } + + /** + * Checks if the field is unique. + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is unique, FALSE otherwise. + */ + public function isUniqueField($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['unique']) && $mapping['unique'] == true; + } + return false; + } + + /** + * Checks if the field is not null. + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is not null, FALSE otherwise. + */ + public function isNullable($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['nullable']) && $mapping['nullable'] == true; + } + return false; + } + + /** + * Gets a column name for a field name. + * If the column name for the field cannot be found, the given field name + * is returned. + * + * @param string $fieldName The field name. + * + * @return string The column name. + */ + public function getColumnName($fieldName) + { + return isset($this->columnNames[$fieldName]) ? + $this->columnNames[$fieldName] : $fieldName; + } + + /** + * Gets the mapping of a (regular) field that holds some data but not a + * reference to another object. + * + * @param string $fieldName The field name. + * + * @return array The field mapping. + * + * @throws MappingException + */ + public function getFieldMapping($fieldName) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->fieldMappings[$fieldName]; + } + + /** + * Gets the mapping of an association. + * + * @see ClassMetadataInfo::$associationMappings + * + * @param string $fieldName The field name that represents the association in + * the object model. + * + * @return array The mapping. + * + * @throws MappingException + */ + public function getAssociationMapping($fieldName) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]; + } + + /** + * Gets all association mappings of the class. + * + * @return array + */ + public function getAssociationMappings() + { + return $this->associationMappings; + } + + /** + * Gets the field name for a column name. + * If no field name can be found the column name is returned. + * + * @param string $columnName The column name. + * + * @return string The column alias. + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Gets the named query. + * + * @see ClassMetadataInfo::$namedQueries + * + * @param string $queryName The query name. + * + * @return string + * + * @throws MappingException + */ + public function getNamedQuery($queryName) + { + if ( ! isset($this->namedQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + return $this->namedQueries[$queryName]['dql']; + } + + /** + * Gets all named queries of the class. + * + * @return array + */ + public function getNamedQueries() + { + return $this->namedQueries; + } + + /** + * Gets the named native query. + * + * @see ClassMetadataInfo::$namedNativeQueries + * + * @param string $queryName The query name. + * + * @return array + * + * @throws MappingException + */ + public function getNamedNativeQuery($queryName) + { + if ( ! isset($this->namedNativeQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + + return $this->namedNativeQueries[$queryName]; + } + + /** + * Gets all named native queries of the class. + * + * @return array + */ + public function getNamedNativeQueries() + { + return $this->namedNativeQueries; + } + + /** + * Gets the result set mapping. + * + * @see ClassMetadataInfo::$sqlResultSetMappings + * + * @param string $name The result set mapping name. + * + * @return array + * + * @throws MappingException + */ + public function getSqlResultSetMapping($name) + { + if ( ! isset($this->sqlResultSetMappings[$name])) { + throw MappingException::resultMappingNotFound($this->name, $name); + } + + return $this->sqlResultSetMappings[$name]; + } + + /** + * Gets all sql result set mappings of the class. + * + * @return array + */ + public function getSqlResultSetMappings() + { + return $this->sqlResultSetMappings; + } + + /** + * Validates & completes the given field mapping. + * + * @param array $mapping The field mapping to validate & complete. + * + * @return array The validated and completed field mapping. + * + * @throws MappingException + */ + protected function _validateAndCompleteFieldMapping(array &$mapping) + { + // Check mandatory fields + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['type'])) { + // Default to string + $mapping['type'] = 'string'; + } + + // Complete fieldName and columnName mapping + if ( ! isset($mapping['columnName'])) { + $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); + } + + if ($mapping['columnName'][0] === '`') { + $mapping['columnName'] = trim($mapping['columnName'], '`'); + $mapping['quoted'] = true; + } + + $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; + if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) { + throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); + } + + $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if ($this->versionField == $mapping['fieldName']) { + throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + $this->identifier[] = $mapping['fieldName']; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) { + if (isset($mapping['id']) && $mapping['id'] === true) { + throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']); + } + + $mapping['requireSQLConversion'] = true; + } + } + + /** + * Validates & completes the basic mapping information that is common to all + * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). + * + * @param array $mapping The mapping. + * + * @return array The updated mapping. + * + * @throws MappingException If something is wrong with the mapping. + */ + protected function _validateAndCompleteAssociationMapping(array $mapping) + { + if ( ! isset($mapping['mappedBy'])) { + $mapping['mappedBy'] = null; + } + if ( ! isset($mapping['inversedBy'])) { + $mapping['inversedBy'] = null; + } + $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy + + // unset optional indexBy attribute if its empty + if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) { + unset($mapping['indexBy']); + } + + // If targetEntity is unqualified, assume it is in the same namespace as + // the sourceEntity. + $mapping['sourceEntity'] = $this->name; + + if (isset($mapping['targetEntity'])) { + $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']); + $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); + } + + if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 && + isset($mapping['orphanRemoval']) && + $mapping['orphanRemoval'] == true) { + + throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); + } + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) { + throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + if (count($mapping['joinColumns']) >= 2) { + throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( + $mapping['targetEntity'], $this->name, $mapping['fieldName'] + ); + } + + $this->identifier[] = $mapping['fieldName']; + $this->containsForeignIdentifier = true; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + // Mandatory attributes for both sides + // Mandatory: fieldName, targetEntity + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['targetEntity'])) { + throw MappingException::missingTargetEntity($mapping['fieldName']); + } + + // Mandatory and optional attributes for either side + if ( ! $mapping['mappedBy']) { + if (isset($mapping['joinTable']) && $mapping['joinTable']) { + if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } + } + } else { + $mapping['isOwningSide'] = false; + } + + if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { + throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']); + } + + // Fetch mode. Default fetch mode to LAZY, if not set. + if ( ! isset($mapping['fetch'])) { + $mapping['fetch'] = self::FETCH_LAZY; + } + + // Cascades + $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array(); + + if (in_array('all', $cascades)) { + $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach'); + } + + if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) { + throw MappingException::invalidCascadeOption( + array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))), + $this->name, + $mapping['fieldName'] + ); + } + + $mapping['cascade'] = $cascades; + $mapping['isCascadeRemove'] = in_array('remove', $cascades); + $mapping['isCascadePersist'] = in_array('persist', $cascades); + $mapping['isCascadeRefresh'] = in_array('refresh', $cascades); + $mapping['isCascadeMerge'] = in_array('merge', $cascades); + $mapping['isCascadeDetach'] = in_array('detach', $cascades); + + return $mapping; + } + + /** + * Validates & completes a one-to-one association mapping. + * + * @param array $mapping The mapping to validate & complete. + * + * @return array The validated & completed mapping. + * + * @throws RuntimeException + * @throws MappingException + */ + protected function _validateAndCompleteOneToOneMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { + $mapping['isOwningSide'] = true; + } + + if ($mapping['isOwningSide']) { + if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) { + // Apply default join column + $mapping['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinColumnName($mapping['fieldName']), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName() + )); + } + + $uniqueConstraintColumns = array(); + foreach ($mapping['joinColumns'] as &$joinColumn) { + if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { + if (count($mapping['joinColumns']) == 1) { + if ( ! isset($mapping['id']) || ! $mapping['id']) { + $joinColumn['unique'] = true; + } + } else { + $uniqueConstraintColumns[] = $joinColumn['name']; + } + } + + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName']); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) + ? $joinColumn['fieldName'] : $joinColumn['name']; + } + + if ($uniqueConstraintColumns) { + if ( ! $this->table) { + throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship."); + } + $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array( + 'columns' => $uniqueConstraintColumns + ); + } + + $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if ($mapping['orphanRemoval']) { + unset($mapping['unique']); + } + + if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { + throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); + } + + return $mapping; + } + + /** + * Validates & completes a one-to-many association mapping. + * + * @param array $mapping The mapping to validate and complete. + * + * @return array The validated and completed mapping. + * + * @throws MappingException + * @throws InvalidArgumentException + */ + protected function _validateAndCompleteOneToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + // OneToMany-side MUST be inverse (must have mappedBy) + if ( ! isset($mapping['mappedBy'])) { + throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + /** + * Validates & completes a many-to-many association mapping. + * + * @param array $mapping The mapping to validate & complete. + * + * @return array The validated & completed mapping. + * + * @throws \InvalidArgumentException + */ + protected function _validateAndCompleteManyToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + if ($mapping['isOwningSide']) { + // owning side MUST have a join table + if ( ! isset($mapping['joinTable']['name'])) { + $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); + } + + $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity'] + && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns']))); + + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + + $mapping['joinTableColumns'] = array(); + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $joinColumn['name']; + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { + if (empty($inverseJoinColumn['name'])) { + $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']); + } + + if (empty($inverseJoinColumn['referencedColumnName'])) { + $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($inverseJoinColumn['name'][0] === '`') { + $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + if ($inverseJoinColumn['referencedColumnName'][0] === '`') { + $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; + } + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierFieldNames() + { + return $this->identifier; + } + + /** + * Gets the name of the single id field. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierFieldName() + { + if ($this->isIdentifierComposite) { + throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); + } + return $this->identifier[0]; + } + + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierColumnName() + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + + /** + * INTERNAL: + * Sets the mapped identifier/primary key fields of this class. + * Mainly used by the ClassMetadataFactory to assign inherited identifiers. + * + * @param array $identifier + * + * @return void + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritDoc} + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Gets an array containing all the column names. + * + * @param array|null $fieldNames + * + * @return array + */ + public function getColumnNames(array $fieldNames = null) + { + if ($fieldNames === null) { + return array_keys($this->fieldNames); + } else { + $columnNames = array(); + foreach ($fieldNames as $fieldName) { + $columnNames[] = $this->getColumnName($fieldName); + } + return $columnNames; + } + } + + /** + * Returns an array with all the identifier column names. + * + * @return array + */ + public function getIdentifierColumnNames() + { + $columnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $columnNames[] = $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns); + + $columnNames = array_merge($columnNames, $assocColumnNames); + } + + return $columnNames; + } + + /** + * Sets the type of Id generator to use for the mapped class. + * + * @param int $generatorType + * + * @return void + */ + public function setIdGeneratorType($generatorType) + { + $this->generatorType = $generatorType; + } + + /** + * Checks whether the mapped class uses an Id generator. + * + * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise. + */ + public function usesIdGenerator() + { + return $this->generatorType != self::GENERATOR_TYPE_NONE; + } + + /** + * @return boolean + */ + public function isInheritanceTypeNone() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the JOINED inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a JOINED inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeJoined() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED; + } + + /** + * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleTable() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + /** + * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeTablePerClass() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Checks whether the class uses an identity column for the Id generation. + * + * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise. + */ + public function isIdGeneratorIdentity() + { + return $this->generatorType == self::GENERATOR_TYPE_IDENTITY; + } + + /** + * Checks whether the class uses a sequence for id generation. + * + * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise. + */ + public function isIdGeneratorSequence() + { + return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE; + } + + /** + * Checks whether the class uses a table for id generation. + * + * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise. + */ + public function isIdGeneratorTable() + { + return $this->generatorType == self::GENERATOR_TYPE_TABLE; + } + + /** + * Checks whether the class has a natural identifier/pk (which means it does + * not use any Id generator. + * + * @return boolean + */ + public function isIdentifierNatural() + { + return $this->generatorType == self::GENERATOR_TYPE_NONE; + } + + /** + * Checks whether the class use a UUID for id generation. + * + * @return boolean + */ + public function isIdentifierUuid() + { + return $this->generatorType == self::GENERATOR_TYPE_UUID; + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * + * @return \Doctrine\DBAL\Types\Type|string|null + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Gets the type of a column. + * + * @param string $columnName + * + * @return \Doctrine\DBAL\Types\Type + */ + public function getTypeOfColumn($columnName) + { + return $this->getTypeOfField($this->getFieldName($columnName)); + } + + /** + * Gets the name of the primary table. + * + * @return string + */ + public function getTableName() + { + return $this->table['name']; + } + + /** + * Gets the table name to use for temporary identifier tables of this class. + * + * @return string + */ + public function getTemporaryIdTableName() + { + // replace dots with underscores because PostgreSQL creates temporary tables in a special schema + return str_replace('.', '_', $this->getTableName() . '_id_tmp'); + } + + /** + * Sets the mapped subclasses of this class. + * + * @param array $subclasses The names of all mapped subclasses. + * + * @return void + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + $this->subClasses[] = $this->fullyQualifiedClassName($subclass); + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + * + * @param array $classNames + * + * @return void + */ + public function setParentClasses(array $classNames) + { + $this->parentClasses = $classNames; + if (count($classNames) > 0) { + $this->rootEntityName = array_pop($classNames); + } + } + + /** + * Sets the inheritance type used by the class and its subclasses. + * + * @param integer $type + * + * @return void + * + * @throws MappingException + */ + public function setInheritanceType($type) + { + if ( ! $this->_isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($this->name, $type); + } + $this->inheritanceType = $type; + } + + /** + * Sets the association to override association mapping of property for an entity relationship. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAssociationOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->associationMappings[$fieldName]; + + if (isset($overrideMapping['joinColumns'])) { + $mapping['joinColumns'] = $overrideMapping['joinColumns']; + } + + if (isset($overrideMapping['joinTable'])) { + $mapping['joinTable'] = $overrideMapping['joinTable']; + } + + $mapping['joinColumnFieldNames'] = null; + $mapping['joinTableColumns'] = null; + $mapping['sourceToTargetKeyColumns'] = null; + $mapping['relationToSourceKeyColumns'] = null; + $mapping['relationToTargetKeyColumns'] = null; + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::ONE_TO_MANY: + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + break; + case self::MANY_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::MANY_TO_MANY: + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + break; + } + + $this->associationMappings[$fieldName] = $mapping; + } + + /** + * Sets the override for a mapped field. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAttributeOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->fieldMappings[$fieldName]; + + if (isset($mapping['id'])) { + $overrideMapping['id'] = $mapping['id']; + } + + if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) { + $overrideMapping['type'] = $mapping['type']; + } + + if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) { + $overrideMapping['fieldName'] = $mapping['fieldName']; + } + + if ($overrideMapping['type'] !== $mapping['type']) { + throw MappingException::invalidOverrideFieldType($this->name, $fieldName); + } + + unset($this->fieldMappings[$fieldName]); + unset($this->fieldNames[$mapping['columnName']]); + unset($this->columnNames[$mapping['fieldName']]); + $this->_validateAndCompleteFieldMapping($overrideMapping); + + $this->fieldMappings[$fieldName] = $overrideMapping; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + * + * @param string $fieldName + * + * @return bool TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Checks if this entity is the root in any entity-inheritance-hierarchy. + * + * @return bool + */ + public function isRootEntity() + { + return $this->name == $this->rootEntityName; + } + + /** + * Checks whether a mapped association field is inherited from a superclass. + * + * @param string $fieldName + * + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]['inherited']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * + * @return void + * + * @deprecated Use {@link setPrimaryTable}. + */ + public function setTableName($tableName) + { + $this->table['name'] = $tableName; + } + + /** + * Sets the primary table definition. The provided array supports the + * following structure: + * + * name => (optional, defaults to class name) + * indexes => array of indexes (optional) + * uniqueConstraints => array of constraints (optional) + * + * If a key is omitted, the current value is kept. + * + * @param array $table The table description. + * + * @return void + */ + public function setPrimaryTable(array $table) + { + if (isset($table['name'])) { + if ($table['name'][0] === '`') { + $table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } + + $this->table['name'] = $table['name']; + } + + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; + } + + if (isset($table['options'])) { + $this->table['options'] = $table['options']; + } + } + + /** + * Checks whether the given type identifies an inheritance type. + * + * @param integer $type + * + * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise. + */ + private function _isInheritanceType($type) + { + return $type == self::INHERITANCE_TYPE_NONE || + $type == self::INHERITANCE_TYPE_SINGLE_TABLE || + $type == self::INHERITANCE_TYPE_JOINED || + $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Adds a mapped field to the class. + * + * @param array $mapping The field mapping. + * + * @return void + * + * @throws MappingException + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']); + } + $this->fieldMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param array $mapping + * + * @return void + * + * @throws MappingException + */ + public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/) + { + if (isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); + } + $this->associationMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + * + * @param array $fieldMapping + * + * @return void + */ + public function addInheritedFieldMapping(array $fieldMapping) + { + $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; + $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; + $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; + } + + /** + * INTERNAL: + * Adds a named query to this class. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + $name = $queryMapping['name']; + $query = $queryMapping['query']; + $dql = str_replace('__CLASS__', $this->name, $query); + $this->namedQueries[$name] = array( + 'name' => $name, + 'query' => $query, + 'dql' => $dql + ); + } + + /** + * INTERNAL: + * Adds a named native query to this class. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedNativeQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedNativeQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) { + throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); + } + + $queryMapping['isSelfClass'] = false; + if (isset($queryMapping['resultClass'])) { + + if($queryMapping['resultClass'] === '__CLASS__') { + + $queryMapping['isSelfClass'] = true; + $queryMapping['resultClass'] = $this->name; + } + + $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']); + $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); + } + + $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; + } + + /** + * INTERNAL: + * Adds a sql result set mapping to this class. + * + * @param array $resultMapping + * + * @return void + * + * @throws MappingException + */ + public function addSqlResultSetMapping(array $resultMapping) + { + if (!isset($resultMapping['name'])) { + throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); + } + + if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { + throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); + } + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityResult) { + if (!isset($entityResult['entityClass'])) { + throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); + } + + $entityResult['isSelfClass'] = false; + if($entityResult['entityClass'] === '__CLASS__') { + + $entityResult['isSelfClass'] = true; + $entityResult['entityClass'] = $this->name; + + } + + $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); + + $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); + $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; + + if (isset($entityResult['fields'])) { + foreach ($entityResult['fields'] as $k => $field) { + if (!isset($field['name'])) { + throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); + } + + if (!isset($field['column'])) { + $fieldName = $field['name']; + if(strpos($fieldName, '.')){ + list(, $fieldName) = explode('.', $fieldName); + } + + $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; + } + } + } + } + } + + $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapOneToOne(array $mapping) + { + $mapping['type'] = self::ONE_TO_ONE; + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a one-to-many mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapOneToMany(array $mapping) + { + $mapping['type'] = self::ONE_TO_MANY; + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-one mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapManyToOne(array $mapping) + { + $mapping['type'] = self::MANY_TO_ONE; + // A many-to-one mapping is essentially a one-one backreference + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-many mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapManyToMany(array $mapping) + { + $mapping['type'] = self::MANY_TO_MANY; + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Stores the association mapping. + * + * @param array $assocMapping + * + * @return void + * + * @throws MappingException + */ + protected function _storeAssociationMapping(array $assocMapping) + { + $sourceFieldName = $assocMapping['fieldName']; + + if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) { + throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName); + } + + $this->associationMappings[$sourceFieldName] = $assocMapping; + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string $repositoryClassName The class name of the custom mapper. + * + * @return void + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker + * + * @param string $lifecycleEvent The lifecycle event. + * @param object $entity The Entity on which the event occurred. + * + * @return void + */ + public function invokeLifecycleCallbacks($lifecycleEvent, $entity) + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + $entity->$callback(); + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + * + * @param string $lifecycleEvent + * + * @return boolean + */ + public function hasLifecycleCallbacks($lifecycleEvent) + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @param string $event + * + * @return array + */ + public function getLifecycleCallbacks($event) + { + return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); + } + + /** + * Adds a lifecycle callback for entities of this class. + * + * @param string $callback + * @param string $event + * + * @return void + */ + public function addLifecycleCallback($callback, $event) + { + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + * + * @return void + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Adds a entity listener for entities of this class. + * + * @param string $eventName The entity lifecycle event. + * @param string $class The listener class. + * @param string $method The listener callback method. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function addEntityListener($eventName, $class, $method) + { + $class = $this->fullyQualifiedClassName($class); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $this->name); + } + + if ( ! method_exists($class, $method)) { + throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); + } + + $this->entityListeners[$eventName][] = array( + 'class' => $class, + 'method' => $method + ); + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * + * @return void + * + * @throws MappingException + * + * @see getDiscriminatorColumn() + */ + public function setDiscriminatorColumn($columnDef) + { + if ($columnDef !== null) { + if ( ! isset($columnDef['name'])) { + throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); + } + + if (isset($this->fieldNames[$columnDef['name']])) { + throw MappingException::duplicateColumnName($this->name, $columnDef['name']); + } + + if ( ! isset($columnDef['fieldName'])) { + $columnDef['fieldName'] = $columnDef['name']; + } + + if ( ! isset($columnDef['type'])) { + $columnDef['type'] = "string"; + } + + if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) { + throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); + } + + $this->discriminatorColumn = $columnDef; + } + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + * + * @return void + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Adds one entry of the discriminator map with a new class and corresponding name. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws MappingException + */ + public function addDiscriminatorMapClass($name, $className) + { + $className = $this->fullyQualifiedClassName($className); + $className = ltrim($className, '\\'); + $this->discriminatorMap[$name] = $className; + + if ($this->name == $className) { + $this->discriminatorValue = $name; + } else { + if ( ! class_exists($className)) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) { + $this->subClasses[] = $className; + } + } + } + + /** + * Checks whether the class has a named query with the given query name. + * + * @param string $queryName + * + * @return boolean + */ + public function hasNamedQuery($queryName) + { + return isset($this->namedQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $queryName + * + * @return boolean + */ + public function hasNamedNativeQuery($queryName) + { + return isset($this->namedNativeQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $name + * + * @return boolean + */ + public function hasSqlResultSetMapping($name) + { + return isset($this->sqlResultSetMappings[$name]); + } + + /** + * {@inheritDoc} + */ + public function hasAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function isSingleValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * {@inheritDoc} + */ + public function isCollectionValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * Is this an association that only has a single join column? + * + * @param string $fieldName + * + * @return bool + */ + public function isAssociationWithSingleJoinColumn($fieldName) + { + return ( + isset($this->associationMappings[$fieldName]) && + isset($this->associationMappings[$fieldName]['joinColumns'][0]) && + !isset($this->associationMappings[$fieldName]['joinColumns'][1]) + ); + } + + /** + * Returns the single association join column (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + public function getSingleAssociationJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; + } + + /** + * Returns the single association referenced join column name (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + public function getSingleAssociationReferencedJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; + } + + /** + * Used to retrieve a fieldname for either field or association from a given column. + * + * This method is used in foreign-key as primary-key contexts. + * + * @param string $columnName + * + * @return string + * + * @throws MappingException + */ + public function getFieldForColumn($columnName) + { + if (isset($this->fieldNames[$columnName])) { + return $this->fieldNames[$columnName]; + } else { + foreach ($this->associationMappings as $assocName => $mapping) { + if ($this->isAssociationWithSingleJoinColumn($assocName) && + $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) { + + return $assocName; + } + } + + throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); + } + } + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator + * + * @return void + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Sets definition. + * + * @param array $definition + * + * @return void + */ + public function setCustomGeneratorDefinition(array $definition) + { + $this->customGeneratorDefinition = $definition; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * 'quoted' => 1 + * ) + * + * + * @param array $definition + * + * @return void + */ + public function setSequenceGeneratorDefinition(array $definition) + { + if (isset($definition['name']) && $definition['name'] == '`') { + $definition['name'] = trim($definition['name'], '`'); + $definition['quoted'] = true; + } + + $this->sequenceGeneratorDefinition = $definition; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @param array $mapping The version field mapping array. + * + * @return void + * + * @throws MappingException + */ + public function setVersionMapping(array &$mapping) + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + + if ( ! isset($mapping['default'])) { + if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) { + $mapping['default'] = 1; + } else if ($mapping['type'] == 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); + } + } + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + * + * @param boolean $bool + * + * @return void + */ + public function setVersioned($bool) + { + $this->isVersioned = $bool; + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + * + * @param string $versionField + * + * @return void + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Marks this class as read only, no change tracking is applied to it. + * + * @return void + */ + public function markReadOnly() + { + $this->isReadOnly = true; + } + + /** + * {@inheritDoc} + */ + public function getFieldNames() + { + return array_keys($this->fieldMappings); + } + + /** + * {@inheritDoc} + */ + public function getAssociationNames() + { + return array_keys($this->associationMappings); + } + + /** + * {@inheritDoc} + * + * @throws InvalidArgumentException + */ + public function getAssociationTargetClass($assocName) + { + if ( ! isset($this->associationMappings[$assocName])) { + throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); + } + + return $this->associationMappings[$assocName]['targetEntity']; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getQuotedIdentifierColumnNames($platform) + { + $quotedColumnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName']) + : $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * Gets the (possibly quoted) column name of a mapped field for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param string $field + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedColumnName($field, $platform) + { + return isset($this->fieldMappings[$field]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) + : $this->fieldMappings[$field]['columnName']; + } + + /** + * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedTableName($platform) + { + return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name']; + } + + /** + * Gets the (possibly quoted) name of the join table. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param array $assoc + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedJoinTableName(array $assoc, $platform) + { + return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; + } + + /** + * {@inheritDoc} + */ + public function isAssociationInverseSide($fieldName) + { + return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide']; + } + + /** + * {@inheritDoc} + */ + public function getAssociationMappedByTargetField($fieldName) + { + return $this->associationMappings[$fieldName]['mappedBy']; + } + + /** + * @param string $targetClass + * + * @return array + */ + public function getAssociationsByTargetClass($targetClass) + { + $relations = array(); + foreach ($this->associationMappings as $mapping) { + if ($mapping['targetEntity'] == $targetClass) { + $relations[$mapping['fieldName']] = $mapping; + } + } + return $relations; + } + + /** + * @param string $className + * @return string + */ + public function fullyQualifiedClassName($className) + { + if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) { + return $this->namespace . '\\' . $className; + } + + return $className; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php new file mode 100644 index 00000000..70337323 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class Column implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var mixed + */ + public $type = 'string'; + + /** + * @var integer + */ + public $length; + + /** + * The precision for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $precision = 0; + + /** + * The scale for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $scale = 0; + + /** + * @var boolean + */ + public $unique = false; + + /** + * @var boolean + */ + public $nullable = false; + + /** + * @var array + */ + public $options = array(); + + /** + * @var string + */ + public $columnDefinition; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php new file mode 100644 index 00000000..a164c85c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References name of a column in the SELECT clause of a SQL query. + * Scalar result types can be included in the query result by specifying this annotation in the metadata. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class ColumnResult implements Annotation +{ + /** + * The name of a column in the SELECT clause of a SQL query. + * + * @var string + */ + public $name; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php new file mode 100644 index 00000000..41e200e1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class CustomIdGenerator implements Annotation +{ + /** + * @var string + */ + public $class; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php new file mode 100644 index 00000000..75658547 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default DefaultEntityListener + * + * @since 2.4 + * @author Fabio B. Silva + */ +class DefaultEntityListenerResolver implements EntityListenerResolver +{ + /** + * @var array Map to store entity listener instances. + */ + private $instances = array(); + + /** + * {@inheritdoc} + */ + public function clear($className = null) + { + if ($className === null) { + $this->instances = array(); + + return; + } + + if (isset($this->instances[$className = trim($className, '\\')])) { + unset($this->instances[$className]); + } + } + + /** + * {@inheritdoc} + */ + public function register($object) + { + if ( ! is_object($object)) { + throw new \InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object))); + } + + $this->instances[get_class($object)] = $object; + } + + /** + * {@inheritdoc} + */ + public function resolve($className) + { + if (isset($this->instances[$className = trim($className, '\\')])) { + return $this->instances[$className]; + } + + return $this->instances[$className] = new $className(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php new file mode 100644 index 00000000..2433b4af --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default NamingStrategy + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultNamingStrategy implements NamingStrategy +{ + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + return substr($className, strrpos($className, '\\') + 1); + } + + return $className; + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $propertyName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName) + { + return $propertyName . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php new file mode 100644 index 00000000..70359de3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -0,0 +1,142 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the physical column, alias and table quotes + * + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($class->fieldMappings[$fieldName]['quoted']) + ? $platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName']) + : $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + return isset($class->table['quoted']) + ? $platform->quoteIdentifier($class->table['name']) + : $class->table['name']; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($definition['quoted']) + ? $platform->quoteIdentifier($definition['sequenceName']) + : $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['referencedColumnName']) + : $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($association['joinTable']['quoted']) + ? $platform->quoteIdentifier($association['joinTable']['name']) + : $association['joinTable']['name']; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + $quotedColumnNames = array(); + + foreach ($class->identifier as $fieldName) { + if (isset($class->fieldMappings[$fieldName])) { + $quotedColumnNames[] = $this->getColumnName($fieldName, $class, $platform); + + continue; + } + + // Association defined as Id field + $joinColumns = $class->associationMappings[$fieldName]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + // 1 ) Concatenate column name and counter + // 2 ) Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + // 3 ) Strip non alphanumeric characters + // 4 ) Prefix with "_" if the result its numeric + $columnName = $columnName . $counter; + $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); + $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + $columnName = is_numeric($columnName) ? '_' . $columnName : $columnName; + + return $platform->getSQLResultCasing($columnName); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php new file mode 100644 index 00000000..97ca7e9b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorColumn implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var integer + */ + public $length; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var mixed + */ + public $fieldName; + + /** + * @var string + */ + public $columnDefinition; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php new file mode 100644 index 00000000..09d61946 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorMap implements Annotation +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 00000000..f9aaddba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,602 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\ORM\Events; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class AnnotationDriver extends AbstractAnnotationDriver +{ + /** + * {@inheritDoc} + */ + protected $entityAnnotationClasses = array( + 'Doctrine\ORM\Mapping\Entity' => 1, + 'Doctrine\ORM\Mapping\MappedSuperclass' => 2, + ); + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $class = $metadata->getReflectionClass(); + if ( ! $class) { + // this happens when running annotation driver in combination with + // static reflection services. This is not the nicest fix + $class = new \ReflectionClass($metadata->name); + } + + $classAnnotations = $this->reader->getClassAnnotations($class); + + if ($classAnnotations) { + foreach ($classAnnotations as $key => $annot) { + if ( ! is_numeric($key)) { + continue; + } + + $classAnnotations[get_class($annot)] = $annot; + } + } + + // Evaluate Entity annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) { + $entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity']; + if ($entityAnnot->repositoryClass !== null) { + $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); + } + if ($entityAnnot->readOnly) { + $metadata->markReadOnly(); + } + } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) { + $mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']; + $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate Table annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) { + $tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table']; + $primaryTable = array( + 'name' => $tableAnnot->name, + 'schema' => $tableAnnot->schema + ); + + if ($tableAnnot->indexes !== null) { + foreach ($tableAnnot->indexes as $indexAnnot) { + $index = array('columns' => $indexAnnot->columns); + + if ( ! empty($indexAnnot->name)) { + $primaryTable['indexes'][$indexAnnot->name] = $index; + } else { + $primaryTable['indexes'][] = $index; + } + } + } + + if ($tableAnnot->uniqueConstraints !== null) { + foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { + $uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns); + + if ( ! empty($uniqueConstraintAnnot->name)) { + $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; + } else { + $primaryTable['uniqueConstraints'][] = $uniqueConstraint; + } + } + } + + if ($tableAnnot->options !== null) { + $primaryTable['options'] = $tableAnnot->options; + } + + $metadata->setPrimaryTable($primaryTable); + } + + // Evaluate NamedNativeQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) { + $namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries']; + + foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { + $metadata->addNamedNativeQuery(array( + 'name' => $namedNativeQuery->name, + 'query' => $namedNativeQuery->query, + 'resultClass' => $namedNativeQuery->resultClass, + 'resultSetMapping' => $namedNativeQuery->resultSetMapping, + )); + } + } + + // Evaluate SqlResultSetMappings annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) { + $sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings']; + + foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { + $entities = array(); + $columns = array(); + foreach ($resultSetMapping->entities as $entityResultAnnot) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => $entityResultAnnot->entityClass, + 'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, + ); + + foreach ($entityResultAnnot->fields as $fieldResultAnnot) { + $entityResult['fields'][] = array( + 'name' => $fieldResultAnnot->name, + 'column' => $fieldResultAnnot->column + ); + } + + $entities[] = $entityResult; + } + + foreach ($resultSetMapping->columns as $columnResultAnnot) { + $columns[] = array( + 'name' => $columnResultAnnot->name, + ); + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping->name, + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + // Evaluate NamedQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) { + $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries']; + + if ( ! is_array($namedQueriesAnnot->value)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + + foreach ($namedQueriesAnnot->value as $namedQuery) { + if ( ! ($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + $metadata->addNamedQuery(array( + 'name' => $namedQuery->name, + 'query' => $namedQuery->query + )); + } + } + + // Evaluate InheritanceType annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) { + $inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate DiscriminatorColumn annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) { + $discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => $discrColumnAnnot->name, + 'type' => $discrColumnAnnot->type, + 'length' => $discrColumnAnnot->length, + 'columnDefinition' => $discrColumnAnnot->columnDefinition + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate DiscriminatorMap annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) { + $discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap']; + $metadata->setDiscriminatorMap($discrMapAnnot->value); + } + } + } + + + // Evaluate DoctrineChangeTrackingPolicy annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) { + $changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy']; + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value)); + } + + // Evaluate annotations on properties/fields + /* @var $property \ReflectionProperty */ + foreach ($class->getProperties() as $property) { + if ($metadata->isMappedSuperclass && ! $property->isPrivate() + || + $metadata->isInheritedField($property->name) + || + $metadata->isInheritedAssociation($property->name)) { + continue; + } + + $mapping = array(); + $mapping['fieldName'] = $property->getName(); + + // Check for JoinColumn/JoinColumns annotations + $joinColumns = array(); + + if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { + $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); + } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) { + foreach ($joinColumnsAnnot->value as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + } + + // Field can only be annotated with one of: + // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany + if ($columnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) { + if ($columnAnnot->type == null) { + throw MappingException::propertyTypeIsRequired($className, $property->getName()); + } + + $mapping = $this->columnToArray($property->getName(), $columnAnnot); + + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); + } + + if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + + // Check for SequenceGenerator/TableGenerator definition + if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) { + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => $seqGeneratorAnnot->sequenceName, + 'allocationSize' => $seqGeneratorAnnot->allocationSize, + 'initialValue' => $seqGeneratorAnnot->initialValue + )); + } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) { + $metadata->setCustomGeneratorDefinition(array( + 'class' => $customGeneratorAnnot->class + )); + } + } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) { + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; + $mapping['joinColumns'] = $joinColumns; + $mapping['mappedBy'] = $oneToOneAnnot->mappedBy; + $mapping['inversedBy'] = $oneToOneAnnot->inversedBy; + $mapping['cascade'] = $oneToOneAnnot->cascade; + $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch); + $metadata->mapOneToOne($mapping); + } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) { + $mapping['mappedBy'] = $oneToManyAnnot->mappedBy; + $mapping['targetEntity'] = $oneToManyAnnot->targetEntity; + $mapping['cascade'] = $oneToManyAnnot->cascade; + $mapping['indexBy'] = $oneToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch); + + if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapOneToMany($mapping); + } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) { + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['joinColumns'] = $joinColumns; + $mapping['cascade'] = $manyToOneAnnot->cascade; + $mapping['inversedBy'] = $manyToOneAnnot->inversedBy; + $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; + $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch); + $metadata->mapManyToOne($mapping); + } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) { + $joinTable = array(); + + if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) { + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + } + + $mapping['joinTable'] = $joinTable; + $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; + $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; + $mapping['inversedBy'] = $manyToManyAnnot->inversedBy; + $mapping['cascade'] = $manyToManyAnnot->cascade; + $mapping['indexBy'] = $manyToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch); + + if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate AssociationOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) { + $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides']; + + foreach ($associationOverridesAnnot->value as $associationOverride) { + $override = array(); + $fieldName = $associationOverride->name; + + // Check for JoinColumn/JoinColumns annotations + if ($associationOverride->joinColumns) { + $joinColumns = array(); + foreach ($associationOverride->joinColumns as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for JoinTable annotations + if ($associationOverride->joinTable) { + $joinTableAnnot = $associationOverride->joinTable; + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate AttributeOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) { + $attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides']; + foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { + $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); + $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); + } + } + + // Evaluate EntityListeners annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) { + $entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners']; + + foreach ($entityListenersAnnot->value as $item) { + $listenerClassName = $metadata->fullyQualifiedClassName($item); + + if ( ! class_exists($listenerClassName)) { + throw MappingException::entityListenerClassNotFound($listenerClassName, $className); + } + + $hasMapping = false; + $listenerClass = new \ReflectionClass($listenerClassName); + /* @var $method \ReflectionMethod */ + foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + // find method callbacks. + $callbacks = $this->getMethodCallbacks($method); + $hasMapping = $hasMapping ?: ( ! empty($callbacks)); + + foreach ($callbacks as $value) { + $metadata->addEntityListener($value[1], $listenerClassName, $value[0]); + } + } + // Evaluate the listener using naming convention. + if ( ! $hasMapping ) { + EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName); + } + } + } + + // Evaluate @HasLifecycleCallbacks annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { + /* @var $method \ReflectionMethod */ + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + // filter for the declaring class only, callbacks from parents will already be registered. + if ($method->getDeclaringClass()->name !== $class->name) { + continue; + } + + foreach ($this->getMethodCallbacks($method) as $value) { + $metadata->addLifecycleCallback($value[0], $value[1]); + } + } + } + } + + /** + * Attempts to resolve the fetch mode. + * + * @param string $className The class name. + * @param string $fetchMode The fetch mode. + * + * @return integer The fetch mode as defined in ClassMetadata. + * + * @throws MappingException If the fetch mode is not valid. + */ + private function getFetchMode($className, $fetchMode) + { + if( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { + throw MappingException::invalidFetchMode($className, $fetchMode); + } + + return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); + } + + /** + * Parses the given method. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodCallbacks(\ReflectionMethod $method) + { + $callbacks = array(); + $annotations = $this->reader->getMethodAnnotations($method); + + foreach ($annotations as $annot) { + if ($annot instanceof \Doctrine\ORM\Mapping\PrePersist) { + $callbacks[] = array($method->name, Events::prePersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostPersist) { + $callbacks[] = array($method->name, Events::postPersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreUpdate) { + $callbacks[] = array($method->name, Events::preUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostUpdate) { + $callbacks[] = array($method->name, Events::postUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreRemove) { + $callbacks[] = array($method->name, Events::preRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostRemove) { + $callbacks[] = array($method->name, Events::postRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostLoad) { + $callbacks[] = array($method->name, Events::postLoad); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreFlush) { + $callbacks[] = array($method->name, Events::preFlush); + } + } + + return $callbacks; + } + + /** + * Parse the given JoinColumn as array + * + * @param JoinColumn $joinColumn + * @return array + */ + private function joinColumnToArray(JoinColumn $joinColumn) + { + return array( + 'name' => $joinColumn->name, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + 'referencedColumnName' => $joinColumn->referencedColumnName, + ); + } + + /** + * Parse the given Column as array + * + * @param string $fieldName + * @param Column $column + * + * @return array + */ + private function columnToArray($fieldName, Column $column) + { + $mapping = array( + 'fieldName' => $fieldName, + 'type' => $column->type, + 'scale' => $column->scale, + 'length' => $column->length, + 'unique' => $column->unique, + 'nullable' => $column->nullable, + 'precision' => $column->precision + ); + + if ($column->options) { + $mapping['options'] = $column->options; + } + + if (isset($column->name)) { + $mapping['columnName'] = $column->name; + } + + if (isset($column->columnDefinition)) { + $mapping['columnDefinition'] = $column->columnDefinition; + } + + return $mapping; + } + + /** + * Factory method for the Annotation Driver. + * + * @param array|string $paths + * @param AnnotationReader|null $reader + * + * @return AnnotationDriver + */ + static public function create($paths = array(), AnnotationReader $reader = null) + { + if ($reader == null) { + $reader = new AnnotationReader(); + } + + return new self($reader, $paths); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php new file mode 100644 index 00000000..007be963 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -0,0 +1,428 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\ORM\Mapping\MappingException; + +/** + * The DatabaseDriver reverse engineers the mapping metadata from a database. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Benjamin Eberlei + */ +class DatabaseDriver implements MappingDriver +{ + /** + * @var AbstractSchemaManager + */ + private $_sm; + + /** + * @var array|null + */ + private $tables = null; + + /** + * @var array + */ + private $classToTableNames = array(); + + /** + * @var array + */ + private $manyToManyTables = array(); + + /** + * @var array + */ + private $classNamesForTables = array(); + + /** + * @var array + */ + private $fieldNamesForColumns = array(); + + /** + * The namespace for the generated entities. + * + * @var string|null + */ + private $namespace; + + /** + * @param AbstractSchemaManager $schemaManager + */ + public function __construct(AbstractSchemaManager $schemaManager) + { + $this->_sm = $schemaManager; + } + + /** + * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager. + * + * @param array $entityTables + * @param array $manyToManyTables + * + * @return void + */ + public function setTables($entityTables, $manyToManyTables) + { + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + foreach ($entityTables as $table) { + $className = $this->getClassNameForTable($table->getName()); + $this->classToTableNames[$className] = $table->getName(); + $this->tables[$table->getName()] = $table; + } + foreach ($manyToManyTables as $table) { + $this->manyToManyTables[$table->getName()] = $table; + } + } + + /** + * @return void + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function reverseEngineerMappingFromDatabase() + { + if ($this->tables !== null) { + return; + } + + $tables = array(); + + foreach ($this->_sm->listTableNames() as $tableName) { + $tables[$tableName] = $this->_sm->listTableDetails($tableName); + } + + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + foreach ($tables as $tableName => $table) { + /* @var $table \Doctrine\DBAL\Schema\Table */ + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $table->getForeignKeys(); + } else { + $foreignKeys = array(); + } + + $allForeignKeyColumns = array(); + foreach ($foreignKeys as $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + if ( ! $table->hasPrimaryKey()) { + throw new MappingException( + "Table " . $table->getName() . " has no primary key. Doctrine does not ". + "support reverse engineering from tables that don't have a primary key." + ); + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + sort($pkColumns); + sort($allForeignKeyColumns); + + if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) { + $this->manyToManyTables[$tableName] = $table; + } else { + // lower-casing is necessary because of Oracle Uppercase Tablenames, + // assumption is lower-case + underscore separated. + $className = $this->getClassNameForTable($tableName); + $this->tables[$tableName] = $table; + $this->classToTableNames[$className] = $tableName; + } + } + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->reverseEngineerMappingFromDatabase(); + + if (!isset($this->classToTableNames[$className])) { + throw new \InvalidArgumentException("Unknown class " . $className); + } + + $tableName = $this->classToTableNames[$className]; + + $metadata->name = $className; + $metadata->table['name'] = $tableName; + + $columns = $this->tables[$tableName]->getColumns(); + $indexes = $this->tables[$tableName]->getIndexes(); + try { + $primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + $primaryKeyColumns = array(); + } + + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $this->tables[$tableName]->getForeignKeys(); + } else { + $foreignKeys = array(); + } + + $allForeignKeyColumns = array(); + foreach ($foreignKeys as $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + foreach ($columns as $column) { + $fieldMapping = array(); + + if (in_array($column->getName(), $allForeignKeyColumns)) { + continue; + } else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) { + $fieldMapping['id'] = true; + } + + $fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false); + $fieldMapping['columnName'] = $column->getName(); + $fieldMapping['type'] = strtolower((string) $column->getType()); + + if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) { + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['fixed'] = $column->getFixed(); + } else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) { + $fieldMapping['unsigned'] = $column->getUnsigned(); + } + $fieldMapping['nullable'] = $column->getNotNull() ? false : true; + + if (isset($fieldMapping['id'])) { + $ids[] = $fieldMapping; + } else { + $fieldMappings[] = $fieldMapping; + } + } + + if ($ids) { + // We need to check for the columns here, because we might have associations as id as well. + if (count($primaryKeyColumns) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($ids as $id) { + $metadata->mapField($id); + } + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + + foreach ($this->manyToManyTables as $manyTable) { + foreach ($manyTable->getForeignKeys() as $foreignKey) { + // foreign key maps to the table of the current entity, many to many association probably exists + if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) { + $myFk = $foreignKey; + $otherFk = null; + foreach ($manyTable->getForeignKeys() as $foreignKey) { + if ($foreignKey != $myFk) { + $otherFk = $foreignKey; + break; + } + } + + if (!$otherFk) { + // the definition of this many to many table does not contain + // enough foreign key information to continue reverse engineering. + continue; + } + + $localColumn = current($myFk->getColumns()); + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName()); + if (current($manyTable->getColumns())->getName() == $localColumn) { + $associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + $associationMapping['joinTable'] = array( + 'name' => strtolower($manyTable->getName()), + 'joinColumns' => array(), + 'inverseJoinColumns' => array(), + ); + + $fkCols = $myFk->getForeignColumns(); + $cols = $myFk->getColumns(); + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + $fkCols = $otherFk->getForeignColumns(); + $cols = $otherFk->getColumns(); + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['inverseJoinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + } else { + $associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + } + $metadata->mapManyToMany($associationMapping); + break; + } + } + } + + foreach ($foreignKeys as $foreignKey) { + $foreignTable = $foreignKey->getForeignTableName(); + $cols = $foreignKey->getColumns(); + $fkCols = $foreignKey->getForeignColumns(); + + $localColumn = current($cols); + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable); + + if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { + $associationMapping['fieldName'] = $associationMapping['fieldName'] . "2"; + } + + if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + //Here we need to check if $cols are the same as $primaryKeyColumns + if (!array_diff($cols,$primaryKeyColumns)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $this->reverseEngineerMappingFromDatabase(); + + return array_keys($this->classToTableNames); + } + + /** + * Sets class name for a table. + * + * @param string $tableName + * @param string $className + * + * @return void + */ + public function setClassNameForTable($tableName, $className) + { + $this->classNamesForTables[$tableName] = $className; + } + + /** + * Sets field name for a column on a specific table. + * + * @param string $tableName + * @param string $columnName + * @param string $fieldName + * + * @return void + */ + public function setFieldNameForColumn($tableName, $columnName, $fieldName) + { + $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName; + } + + /** + * Returns the mapped class name for a table if it exists. Otherwise return "classified" version. + * + * @param string $tableName + * + * @return string + */ + private function getClassNameForTable($tableName) + { + if (isset($this->classNamesForTables[$tableName])) { + return $this->namespace . $this->classNamesForTables[$tableName]; + } + + return $this->namespace . Inflector::classify(strtolower($tableName)); + } + + /** + * Return the mapped field name for a column, if it exists. Otherwise return camelized version. + * + * @param string $tableName + * @param string $columnName + * @param boolean $fk Whether the column is a foreignkey or not. + * + * @return string + */ + private function getFieldNameForColumn($tableName, $columnName, $fk = false) + { + if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) { + return $this->fieldNamesForColumns[$tableName][$columnName]; + } + + $columnName = strtolower($columnName); + + // Replace _id if it is a foreignkey column + if ($fk) { + $columnName = str_replace('_id', '', $columnName); + } + return Inflector::camelize($columnName); + } + + /** + * Set the namespace for the generated entities. + * + * @param string $namespace + * + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php new file mode 100644 index 00000000..14abadb9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -0,0 +1,67 @@ +. + */ + +require_once __DIR__.'/../Annotation.php'; +require_once __DIR__.'/../Entity.php'; +require_once __DIR__.'/../MappedSuperclass.php'; +require_once __DIR__.'/../InheritanceType.php'; +require_once __DIR__.'/../DiscriminatorColumn.php'; +require_once __DIR__.'/../DiscriminatorMap.php'; +require_once __DIR__.'/../Id.php'; +require_once __DIR__.'/../GeneratedValue.php'; +require_once __DIR__.'/../Version.php'; +require_once __DIR__.'/../JoinColumn.php'; +require_once __DIR__.'/../JoinColumns.php'; +require_once __DIR__.'/../Column.php'; +require_once __DIR__.'/../OneToOne.php'; +require_once __DIR__.'/../OneToMany.php'; +require_once __DIR__.'/../ManyToOne.php'; +require_once __DIR__.'/../ManyToMany.php'; +require_once __DIR__.'/../ElementCollection.php'; +require_once __DIR__.'/../Table.php'; +require_once __DIR__.'/../UniqueConstraint.php'; +require_once __DIR__.'/../Index.php'; +require_once __DIR__.'/../JoinTable.php'; +require_once __DIR__.'/../SequenceGenerator.php'; +require_once __DIR__.'/../CustomIdGenerator.php'; +require_once __DIR__.'/../ChangeTrackingPolicy.php'; +require_once __DIR__.'/../OrderBy.php'; +require_once __DIR__.'/../NamedQueries.php'; +require_once __DIR__.'/../NamedQuery.php'; +require_once __DIR__.'/../HasLifecycleCallbacks.php'; +require_once __DIR__.'/../PrePersist.php'; +require_once __DIR__.'/../PostPersist.php'; +require_once __DIR__.'/../PreUpdate.php'; +require_once __DIR__.'/../PostUpdate.php'; +require_once __DIR__.'/../PreRemove.php'; +require_once __DIR__.'/../PostRemove.php'; +require_once __DIR__.'/../PostLoad.php'; +require_once __DIR__.'/../PreFlush.php'; +require_once __DIR__.'/../FieldResult.php'; +require_once __DIR__.'/../ColumnResult.php'; +require_once __DIR__.'/../EntityResult.php'; +require_once __DIR__.'/../NamedNativeQuery.php'; +require_once __DIR__.'/../NamedNativeQueries.php'; +require_once __DIR__.'/../SqlResultSetMapping.php'; +require_once __DIR__.'/../SqlResultSetMappings.php'; +require_once __DIR__.'/../AssociationOverride.php'; +require_once __DIR__.'/../AssociationOverrides.php'; +require_once __DIR__.'/../AttributeOverride.php'; +require_once __DIR__.'/../AttributeOverrides.php'; +require_once __DIR__.'/../EntityListeners.php'; \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php new file mode 100644 index 00000000..f4cd8cd6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain instead + */ +class DriverChain extends MappingDriverChain +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php new file mode 100644 index 00000000..28e2dbea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver as CommonPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver instead + */ +class PHPDriver extends CommonPHPDriver +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php new file mode 100644 index 00000000..85221f3a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * XmlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedXmlDriver extends XmlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php new file mode 100644 index 00000000..d2b8c0fc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * YamlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedYamlDriver extends YamlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 00000000..d6c6ead8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver as CommonStaticPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver instead + */ +class StaticPHPDriver extends CommonStaticPHPDriver +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php new file mode 100644 index 00000000..ed4386ac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -0,0 +1,760 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use SimpleXMLElement; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class XmlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + /* @var $xmlRoot SimpleXMLElement */ + $xmlRoot = $this->getElement($className); + + if ($xmlRoot->getName() == 'entity') { + if (isset($xmlRoot['repository-class'])) { + $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']); + } + if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) { + $metadata->markReadOnly(); + } + } else if ($xmlRoot->getName() == 'mapped-superclass') { + $metadata->setCustomRepositoryClass( + isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null + ); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate attributes + $table = array(); + if (isset($xmlRoot['table'])) { + $table['name'] = (string)$xmlRoot['table']; + } + + $metadata->setPrimaryTable($table); + + // Evaluate named queries + if (isset($xmlRoot->{'named-queries'})) { + foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) { + $metadata->addNamedQuery(array( + 'name' => (string)$namedQueryElement['name'], + 'query' => (string)$namedQueryElement['query'] + )); + } + } + + // Evaluate native named queries + if (isset($xmlRoot->{'named-native-queries'})) { + foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) { + $metadata->addNamedNativeQuery(array( + 'name' => isset($nativeQueryElement['name']) ? (string)$nativeQueryElement['name'] : null, + 'query' => isset($nativeQueryElement->query) ? (string)$nativeQueryElement->query : null, + 'resultClass' => isset($nativeQueryElement['result-class']) ? (string)$nativeQueryElement['result-class'] : null, + 'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string)$nativeQueryElement['result-set-mapping'] : null, + )); + } + } + + // Evaluate sql result set mapping + if (isset($xmlRoot->{'sql-result-set-mappings'})) { + foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) { + $entities = array(); + $columns = array(); + foreach ($rsmElement as $entityElement) { + // + if (isset($entityElement['entity-class'])) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => (string)$entityElement['entity-class'], + 'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string)$entityElement['discriminator-column'] : null, + ); + + foreach ($entityElement as $fieldElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldElement['name']) ? (string)$fieldElement['name'] : null, + 'column' => isset($fieldElement['column']) ? (string)$fieldElement['column'] : null, + ); + } + + $entities[] = $entityResult; + } + + // + if (isset($entityElement['name'])) { + $columns[] = array( + 'name' => (string)$entityElement['name'], + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => (string)$rsmElement['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($xmlRoot['schema'])) { + $metadata->table['schema'] = (string)$xmlRoot['schema']; + }*/ + + if (isset($xmlRoot['inheritance-type'])) { + $inheritanceType = (string)$xmlRoot['inheritance-type']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate + if (isset($xmlRoot->{'discriminator-column'})) { + $discrColumn = $xmlRoot->{'discriminator-column'}; + $metadata->setDiscriminatorColumn(array( + 'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['column-definition']) ? (string)$discrColumn['column-definition'] : null + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate + if (isset($xmlRoot->{'discriminator-map'})) { + $map = array(); + foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) { + $map[(string)$discrMapElement['value']] = (string)$discrMapElement['class']; + } + $metadata->setDiscriminatorMap($map); + } + } + } + + + // Evaluate + if (isset($xmlRoot['change-tracking-policy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper((string)$xmlRoot['change-tracking-policy']))); + } + + // Evaluate + if (isset($xmlRoot->indexes)) { + $metadata->table['indexes'] = array(); + foreach ($xmlRoot->indexes->index as $index) { + $columns = explode(',', (string)$index['columns']); + + if (isset($index['name'])) { + $metadata->table['indexes'][(string)$index['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['indexes'][] = array( + 'columns' => $columns + ); + } + } + } + + // Evaluate + if (isset($xmlRoot->{'unique-constraints'})) { + $metadata->table['uniqueConstraints'] = array(); + foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) { + $columns = explode(',', (string)$unique['columns']); + + if (isset($unique['name'])) { + $metadata->table['uniqueConstraints'][(string)$unique['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['uniqueConstraints'][] = array( + 'columns' => $columns + ); + } + } + } + + if (isset($xmlRoot->options)) { + $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children()); + } + + // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions + // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception + $mappings = array(); + // Evaluate mappings + if (isset($xmlRoot->field)) { + foreach ($xmlRoot->field as $fieldMapping) { + $mapping = $this->columnToArray($fieldMapping); + + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + foreach ($mappings as $mapping) { + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + } + + // Evaluate mappings + $associationIds = array(); + foreach ($xmlRoot->id as $idElement) { + if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) { + $associationIds[(string)$idElement['name']] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => (string)$idElement['name'] + ); + + if (isset($idElement['type'])) { + $mapping['type'] = (string)$idElement['type']; + } + + if (isset($idElement['length'])) { + $mapping['length'] = (string)$idElement['length']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = (string)$idElement['column']; + } + + if (isset($idElement['column-definition'])) { + $mapping['columnDefinition'] = (string)$idElement['column-definition']; + } + + $metadata->mapField($mapping); + + if (isset($idElement->generator)) { + $strategy = isset($idElement->generator['strategy']) ? + (string)$idElement->generator['strategy'] : 'AUTO'; + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . $strategy)); + } + + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement->{'sequence-generator'})) { + $seqGenerator = $idElement->{'sequence-generator'}; + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => (string)$seqGenerator['sequence-name'], + 'allocationSize' => (string)$seqGenerator['allocation-size'], + 'initialValue' => (string)$seqGenerator['initial-value'] + )); + } else if (isset($idElement->{'custom-id-generator'})) { + $customGenerator = $idElement->{'custom-id-generator'}; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } else if (isset($idElement->{'table-generator'})) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-one'})) { + foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) { + $mapping = array( + 'fieldName' => (string)$oneToOneElement['field'], + 'targetEntity' => (string)$oneToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$oneToOneElement['mapped-by']; + } else { + if (isset($oneToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$oneToOneElement['inversed-by']; + } + $joinColumns = array(); + + if (isset($oneToOneElement->{'join-column'})) { + $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'}); + } else if (isset($oneToOneElement->{'join-columns'})) { + foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade); + } + + if (isset($oneToOneElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']); + } + + $metadata->mapOneToOne($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-many'})) { + foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) { + $mapping = array( + 'fieldName' => (string)$oneToManyElement['field'], + 'targetEntity' => (string)$oneToManyElement['target-entity'], + 'mappedBy' => (string)$oneToManyElement['mapped-by'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']); + } + + if (isset($oneToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade); + } + + if (isset($oneToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']); + } + + if (isset($oneToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($oneToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$oneToManyElement['index-by']; + } else if (isset($oneToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapOneToMany($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-one'})) { + foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) { + $mapping = array( + 'fieldName' => (string)$manyToOneElement['field'], + 'targetEntity' => (string)$manyToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToOneElement['inversed-by']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement->{'join-column'})) { + $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'}); + } else if (isset($manyToOneElement->{'join-columns'})) { + foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade); + } + + $metadata->mapManyToOne($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-many'})) { + foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) { + $mapping = array( + 'fieldName' => (string)$manyToManyElement['field'], + 'targetEntity' => (string)$manyToManyElement['target-entity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']); + } + + if (isset($manyToManyElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$manyToManyElement['mapped-by']; + } else if (isset($manyToManyElement->{'join-table'})) { + if (isset($manyToManyElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToManyElement['inversed-by']; + } + + $joinTableElement = $manyToManyElement->{'join-table'}; + $joinTable = array( + 'name' => (string)$joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = (string)$joinTableElement['schema']; + } + + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade); + } + + if (isset($manyToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($manyToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$manyToManyElement['index-by']; + } else if (isset($manyToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'attribute-overrides'})) { + foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + foreach ($overrideElement->field as $field) { + $mapping = $this->columnToArray($field); + $mapping['fieldName'] = $fieldName; + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'association-overrides'})) { + foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + $override = array(); + + // Check for join-columns + if (isset($overrideElement->{'join-columns'})) { + $joinColumns = array(); + foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for join-table + if ($overrideElement->{'join-table'}) { + $joinTable = null; + $joinTableElement = $overrideElement->{'join-table'}; + + $joinTable = array( + 'name' => (string) $joinTableElement['name'], + 'schema' => (string) $joinTableElement['schema'] + ); + + if (isset($joinTableElement->{'join-columns'})) { + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement->{'inverse-join-columns'})) { + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate + if (isset($xmlRoot->{'lifecycle-callbacks'})) { + foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { + $metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type'])); + } + } + + // Evaluate entity listener + if (isset($xmlRoot->{'entity-listeners'})) { + foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) { + $className = (string) $listenerElement['class']; + // Evaluate the listener using naming convention. + if($listenerElement->count() === 0) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($listenerElement as $callbackElement) { + $eventName = (string) $callbackElement['type']; + $methodName = (string) $callbackElement['method']; + + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + + /** + * Parses (nested) option elements. + * + * @param SimpleXMLElement $options The XML element. + * + * @return array The options array. + */ + private function _parseOptions(SimpleXMLElement $options) + { + $array = array(); + + /* @var $option SimpleXMLElement */ + foreach ($options as $option) { + if ($option->count()) { + $value = $this->_parseOptions($option->children()); + } else { + $value = (string) $option; + } + + $attr = $option->attributes(); + + if (isset($attr->name)) { + $array[(string) $attr->name] = $value; + } else { + $array[] = $value; + } + } + + return $array; + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given SimpleXMLElement. + * + * @param SimpleXMLElement $joinColumnElement The XML element. + * + * @return array The mapping array. + */ + private function joinColumnToArray(SimpleXMLElement $joinColumnElement) + { + $joinColumn = array( + 'name' => (string)$joinColumnElement['name'], + 'referencedColumnName' => (string)$joinColumnElement['referenced-column-name'] + ); + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']); + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']); + } + + if (isset($joinColumnElement['on-delete'])) { + $joinColumn['onDelete'] = (string)$joinColumnElement['on-delete']; + } + + if (isset($joinColumnElement['column-definition'])) { + $joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition']; + } + + return $joinColumn; + } + + /** + * Parses the given field as array. + * + * @param SimpleXMLElement $fieldMapping + * + * @return array + */ + private function columnToArray(SimpleXMLElement $fieldMapping) + { + $mapping = array( + 'fieldName' => (string) $fieldMapping['name'], + ); + + if (isset($fieldMapping['type'])) { + $mapping['type'] = (string) $fieldMapping['type']; + } + + if (isset($fieldMapping['column'])) { + $mapping['columnName'] = (string) $fieldMapping['column']; + } + + if (isset($fieldMapping['length'])) { + $mapping['length'] = (int) $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $mapping['precision'] = (int) $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $mapping['scale'] = (int) $fieldMapping['scale']; + } + + if (isset($fieldMapping['unique'])) { + $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']); + } + + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']); + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']); + } + + if (isset($fieldMapping['column-definition'])) { + $mapping['columnDefinition'] = (string) $fieldMapping['column-definition']; + } + + if (isset($fieldMapping->options)) { + $mapping['options'] = $this->_parseOptions($fieldMapping->options->children()); + } + + return $mapping; + } + + /** + * Gathers a list of cascade options found in the given cascade element. + * + * @param SimpleXMLElement $cascadeElement The cascade element. + * + * @return array The list of cascade options. + */ + private function _getCascadeMappings($cascadeElement) + { + $cascades = array(); + /* @var $action SimpleXmlElement */ + foreach ($cascadeElement->children() as $action) { + // According to the JPA specifications, XML uses "cascade-persist" + // instead of "persist". Here, both variations + // are supported because both YAML and Annotation use "persist" + // and we want to make sure that this driver doesn't need to know + // anything about the supported cascading actions + $cascades[] = str_replace('cascade-', '', $action->getName()); + } + return $cascades; + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $result = array(); + $xmlElement = simplexml_load_file($file); + + if (isset($xmlElement->entity)) { + foreach ($xmlElement->entity as $entityElement) { + $entityName = (string)$entityElement['name']; + $result[$entityName] = $entityElement; + } + } else if (isset($xmlElement->{'mapped-superclass'})) { + foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { + $className = (string)$mappedSuperClass['name']; + $result[$className] = $mappedSuperClass; + } + } + + return $result; + } + + /** + * @param mixed $element + * + * @return bool + */ + protected function evaluateBoolean($element) + { + $flag = (string)$element; + + return ($flag === true || $flag == "true" || $flag == "1"); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php new file mode 100644 index 00000000..9e8818a1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -0,0 +1,710 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Yaml\Yaml; + +/** + * The YamlDriver reads the mapping metadata from yaml schema files. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class YamlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $element = $this->getElement($className); + + if ($element['type'] == 'entity') { + if (isset($element['repositoryClass'])) { + $metadata->setCustomRepositoryClass($element['repositoryClass']); + } + if (isset($element['readOnly']) && $element['readOnly'] == true) { + $metadata->markReadOnly(); + } + } else if ($element['type'] == 'mappedSuperclass') { + $metadata->setCustomRepositoryClass( + isset($element['repositoryClass']) ? $element['repositoryClass'] : null + ); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate root level properties + $table = array(); + if (isset($element['table'])) { + $table['name'] = $element['table']; + } + $metadata->setPrimaryTable($table); + + // Evaluate named queries + if (isset($element['namedQueries'])) { + foreach ($element['namedQueries'] as $name => $queryMapping) { + if (is_string($queryMapping)) { + $queryMapping = array('query' => $queryMapping); + } + + if ( ! isset($queryMapping['name'])) { + $queryMapping['name'] = $name; + } + + $metadata->addNamedQuery($queryMapping); + } + } + + // Evaluate named native queries + if (isset($element['namedNativeQueries'])) { + foreach ($element['namedNativeQueries'] as $name => $mappingElement) { + if (!isset($mappingElement['name'])) { + $mappingElement['name'] = $name; + } + $metadata->addNamedNativeQuery(array( + 'name' => $mappingElement['name'], + 'query' => isset($mappingElement['query']) ? $mappingElement['query'] : null, + 'resultClass' => isset($mappingElement['resultClass']) ? $mappingElement['resultClass'] : null, + 'resultSetMapping' => isset($mappingElement['resultSetMapping']) ? $mappingElement['resultSetMapping'] : null, + )); + } + } + + // Evaluate sql result set mappings + if (isset($element['sqlResultSetMappings'])) { + foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) { + if (!isset($resultSetMapping['name'])) { + $resultSetMapping['name'] = $name; + } + + $entities = array(); + $columns = array(); + if (isset($resultSetMapping['entityResult'])) { + foreach ($resultSetMapping['entityResult'] as $entityResultElement) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => isset($entityResultElement['entityClass']) ? $entityResultElement['entityClass'] : null, + 'discriminatorColumn' => isset($entityResultElement['discriminatorColumn']) ? $entityResultElement['discriminatorColumn'] : null, + ); + + if (isset($entityResultElement['fieldResult'])) { + foreach ($entityResultElement['fieldResult'] as $fieldResultElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldResultElement['name']) ? $fieldResultElement['name'] : null, + 'column' => isset($fieldResultElement['column']) ? $fieldResultElement['column'] : null, + ); + } + } + + $entities[] = $entityResult; + } + } + + + if (isset($resultSetMapping['columnResult'])) { + foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) { + $columns[] = array( + 'name' => isset($columnResultAnnot['name']) ? $columnResultAnnot['name'] : null, + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($element['schema'])) { + $metadata->table['schema'] = $element['schema']; + }*/ + + if (isset($element['inheritanceType'])) { + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType']))); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate discriminatorColumn + if (isset($element['discriminatorColumn'])) { + $discrColumn = $element['discriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string)$discrColumn['columnDefinition'] : null + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate discriminatorMap + if (isset($element['discriminatorMap'])) { + $metadata->setDiscriminatorMap($element['discriminatorMap']); + } + } + } + + + // Evaluate changeTrackingPolicy + if (isset($element['changeTrackingPolicy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper($element['changeTrackingPolicy']))); + } + + // Evaluate indexes + if (isset($element['indexes'])) { + foreach ($element['indexes'] as $name => $index) { + if ( ! isset($index['name'])) { + $index['name'] = $name; + } + + if (is_string($index['columns'])) { + $columns = explode(',', $index['columns']); + $columns = array_map('trim', $columns); + } else { + $columns = $index['columns']; + } + + $metadata->table['indexes'][$index['name']] = array( + 'columns' => $columns + ); + } + } + + // Evaluate uniqueConstraints + if (isset($element['uniqueConstraints'])) { + foreach ($element['uniqueConstraints'] as $name => $unique) { + if ( ! isset($unique['name'])) { + $unique['name'] = $name; + } + + if (is_string($unique['columns'])) { + $columns = explode(',', $unique['columns']); + $columns = array_map('trim', $columns); + } else { + $columns = $unique['columns']; + } + + $metadata->table['uniqueConstraints'][$unique['name']] = array( + 'columns' => $columns + ); + } + } + + if (isset($element['options'])) { + $metadata->table['options'] = $element['options']; + } + + $associationIds = array(); + if (isset($element['id'])) { + // Evaluate identifier settings + foreach ($element['id'] as $name => $idElement) { + if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) { + $associationIds[$name] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => $name + ); + + if (isset($idElement['type'])) { + $mapping['type'] = $idElement['type']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = $idElement['column']; + } + + if (isset($idElement['length'])) { + $mapping['length'] = $idElement['length']; + } + + if (isset($idElement['columnDefinition'])) { + $mapping['columnDefinition'] = $idElement['columnDefinition']; + } + + $metadata->mapField($mapping); + + if (isset($idElement['generator'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($idElement['generator']['strategy']))); + } + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement['sequenceGenerator'])) { + $metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']); + } else if (isset($idElement['customIdGenerator'])) { + $customGenerator = $idElement['customIdGenerator']; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } else if (isset($idElement['tableGenerator'])) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + } + + // Evaluate fields + if (isset($element['fields'])) { + foreach ($element['fields'] as $name => $fieldMapping) { + + $mapping = $this->columnToArray($name, $fieldMapping); + + if (isset($fieldMapping['id'])) { + $mapping['id'] = true; + if (isset($fieldMapping['generator']['strategy'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($fieldMapping['generator']['strategy']))); + } + } + + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + + // Evaluate oneToOne relationships + if (isset($element['oneToOne'])) { + foreach ($element['oneToOne'] as $name => $oneToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mappedBy'])) { + $mapping['mappedBy'] = $oneToOneElement['mappedBy']; + } else { + if (isset($oneToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $oneToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($oneToOneElement['joinColumn'])) { + $joinColumns[] = $this->joinColumnToArray($oneToOneElement['joinColumn']); + } else if (isset($oneToOneElement['joinColumns'])) { + foreach ($oneToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement['cascade'])) { + $mapping['cascade'] = $oneToOneElement['cascade']; + } + + if (isset($oneToOneElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval']; + } + + $metadata->mapOneToOne($mapping); + } + } + + // Evaluate oneToMany relationships + if (isset($element['oneToMany'])) { + foreach ($element['oneToMany'] as $name => $oneToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToManyElement['targetEntity'], + 'mappedBy' => $oneToManyElement['mappedBy'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']); + } + + if (isset($oneToManyElement['cascade'])) { + $mapping['cascade'] = $oneToManyElement['cascade']; + } + + if (isset($oneToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval']; + } + + if (isset($oneToManyElement['orderBy'])) { + $mapping['orderBy'] = $oneToManyElement['orderBy']; + } + + if (isset($oneToManyElement['indexBy'])) { + $mapping['indexBy'] = $oneToManyElement['indexBy']; + } + + $metadata->mapOneToMany($mapping); + } + } + + // Evaluate manyToOne relationships + if (isset($element['manyToOne'])) { + foreach ($element['manyToOne'] as $name => $manyToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement['joinColumn'])) { + $joinColumns[] = $this->joinColumnToArray($manyToOneElement['joinColumn']); + } else if (isset($manyToOneElement['joinColumns'])) { + foreach ($manyToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement['cascade'])) { + $mapping['cascade'] = $manyToOneElement['cascade']; + } + + $metadata->mapManyToOne($mapping); + } + } + + // Evaluate manyToMany relationships + if (isset($element['manyToMany'])) { + foreach ($element['manyToMany'] as $name => $manyToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToManyElement['targetEntity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['mappedBy'])) { + $mapping['mappedBy'] = $manyToManyElement['mappedBy']; + } else if (isset($manyToManyElement['joinTable'])) { + + $joinTableElement = $manyToManyElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToManyElement['inversedBy']; + } + + if (isset($manyToManyElement['cascade'])) { + $mapping['cascade'] = $manyToManyElement['cascade']; + } + + if (isset($manyToManyElement['orderBy'])) { + $mapping['orderBy'] = $manyToManyElement['orderBy']; + } + + if (isset($manyToManyElement['indexBy'])) { + $mapping['indexBy'] = $manyToManyElement['indexBy']; + } + + if (isset($manyToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval']; + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate associationOverride + if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { + + foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { + $override = array(); + + // Check for joinColumn + if (isset($associationOverrideElement['joinColumn'])) { + $joinColumns = array(); + foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for joinTable + if (isset($associationOverrideElement['joinTable'])) { + + $joinTableElement = $associationOverrideElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate associationOverride + if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) { + + foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) { + $mapping = $this->columnToArray($fieldName, $attributeOverrideElement); + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + + // Evaluate lifeCycleCallbacks + if (isset($element['lifecycleCallbacks'])) { + foreach ($element['lifecycleCallbacks'] as $type => $methods) { + foreach ($methods as $method) { + $metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type)); + } + } + } + + // Evaluate entityListeners + if (isset($element['entityListeners'])) { + foreach ($element['entityListeners'] as $className => $entityListener) { + // Evaluate the listener using naming convention. + if (empty($entityListener)) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($entityListener as $eventName => $callbackElement){ + foreach ($callbackElement as $methodName){ + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given join column element. + * + * @param array $joinColumnElement The array join column element. + * + * @return array The mapping array. + */ + private function joinColumnToArray($joinColumnElement) + { + $joinColumn = array(); + if (isset($joinColumnElement['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName']; + } + + if (isset($joinColumnElement['name'])) { + $joinColumn['name'] = (string) $joinColumnElement['name']; + } + + if (isset($joinColumnElement['fieldName'])) { + $joinColumn['fieldName'] = (string) $joinColumnElement['fieldName']; + } + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = (bool) $joinColumnElement['unique']; + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = (bool) $joinColumnElement['nullable']; + } + + if (isset($joinColumnElement['onDelete'])) { + $joinColumn['onDelete'] = $joinColumnElement['onDelete']; + } + + if (isset($joinColumnElement['columnDefinition'])) { + $joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition']; + } + + return $joinColumn; + } + + /** + * Parses the given column as array. + * + * @param string $fieldName + * @param array $column + * + * @return array + */ + private function columnToArray($fieldName, $column) + { + $mapping = array( + 'fieldName' => $fieldName + ); + + if (isset($column['type'])) { + $params = explode('(', $column['type']); + $column['type'] = $params[0]; + $mapping['type'] = $column['type']; + + if (isset($params[1])) { + $column['length'] = (integer) substr($params[1], 0, strlen($params[1]) - 1); + } + } + + if (isset($column['column'])) { + $mapping['columnName'] = $column['column']; + } + + if (isset($column['length'])) { + $mapping['length'] = $column['length']; + } + + if (isset($column['precision'])) { + $mapping['precision'] = $column['precision']; + } + + if (isset($column['scale'])) { + $mapping['scale'] = $column['scale']; + } + + if (isset($column['unique'])) { + $mapping['unique'] = (bool)$column['unique']; + } + + if (isset($column['options'])) { + $mapping['options'] = $column['options']; + } + + if (isset($column['nullable'])) { + $mapping['nullable'] = $column['nullable']; + } + + if (isset($column['version']) && $column['version']) { + $mapping['version'] = $column['version']; + } + + if (isset($column['columnDefinition'])) { + $mapping['columnDefinition'] = $column['columnDefinition']; + } + + return $mapping; + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return Yaml::parse($file); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php new file mode 100644 index 00000000..46720d68 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ALL") + * @todo check available targets + */ +final class ElementCollection implements Annotation +{ + /** + * @var string + */ + public $tableName; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php new file mode 100644 index 00000000..edf6ad5a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Entity implements Annotation +{ + /** + * @var string + */ + public $repositoryClass; + + /** + * @var boolean + */ + public $readOnly = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php new file mode 100644 index 00000000..2d5ecf71 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A resolver is used to instantiate an entity listener. + * + * @since 2.4 + * @author Fabio B. Silva + */ +interface EntityListenerResolver +{ + /** + * Clear all instances from the set, or a specific class when given. + * + * @param string $className The fully-qualified class name + * + * @return void + */ + function clear($className = null); + + /** + * Returns a entity listener instance for the given class name. + * + * @param string $className The fully-qualified class name + * + * @return object An entity listener + */ + function resolve($className); + + /** + * Register a entity listener instance. + * + * @param object $object An entity listener + */ + function register($object); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php new file mode 100644 index 00000000..d9478a49 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The EntityListeners annotation specifies the callback listener classes to be used for an entity or mapped superclass. + * The EntityListeners annotation may be applied to an entity class or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.4 + * + * @Annotation + * @Target("CLASS") + */ +final class EntityListeners implements Annotation +{ + /** + * Specifies the names of the entity listeners. + * + * @var array + */ + public $value = array(); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php new file mode 100644 index 00000000..63bbed22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References an entity in the SELECT clause of a SQL query. + * If this annotation is used, the SQL statement should select all of the columns that are mapped to the entity object. + * This should include foreign key columns to related entities. + * The results obtained when insufficient data is available are undefined. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class EntityResult implements Annotation +{ + /** + * The class of the result. + * + * @var string + */ + public $entityClass; + + /** + * Maps the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @var array<\Doctrine\ORM\Mapping\FieldResult> + */ + public $fields = array(); + + /** + * Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance. + * + * @var string + */ + public $discriminatorColumn; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php new file mode 100644 index 00000000..5e8aa0cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to map the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class FieldResult implements Annotation +{ + /** + * Name of the column in the SELECT clause. + * + * @var string + */ + public $name; + + /** + * Name of the persistent field or property of the class. + * + * @var string + */ + public $column; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php new file mode 100644 index 00000000..27c03d4b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class GeneratedValue implements Annotation +{ + /** + * The type of Id generator. + * + * @var string + * + * @Enum({"AUTO", "SEQUENCE", "TABLE", "IDENTITY", "NONE", "UUID", "CUSTOM"}) + */ + public $strategy = 'AUTO'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php new file mode 100644 index 00000000..313ece31 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class HasLifecycleCallbacks implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php new file mode 100644 index 00000000..6c9bcef0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Id implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php new file mode 100644 index 00000000..a6696c4b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class Index implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php new file mode 100644 index 00000000..de803369 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class InheritanceType implements Annotation +{ + /** + * The inheritance type used by the class and its subclasses. + * + * @var string + * + * @Enum({"NONE", "JOINED", "SINGLE_TABLE", "TABLE_PER_CLASS"}) + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php new file mode 100644 index 00000000..febce917 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class JoinColumn implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $referencedColumnName = 'id'; + + /** + * @var boolean + */ + public $unique = false; + + /** + * @var boolean + */ + public $nullable = true; + + /** + * @var mixed + */ + public $onDelete; + + /** + * @var string + */ + public $columnDefinition; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var string + */ + public $fieldName; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php new file mode 100644 index 00000000..ae096c27 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class JoinColumns implements Annotation +{ + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php new file mode 100644 index 00000000..8a440c61 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class JoinTable implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $schema; + + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns = array(); + + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $inverseJoinColumns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php new file mode 100644 index 00000000..ca2f53c9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToMany implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $inversedBy; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; + + /** + * @var string + */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php new file mode 100644 index 00000000..d3414e6a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToOne implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var string + */ + public $inversedBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php new file mode 100644 index 00000000..74588107 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class MappedSuperclass implements Annotation +{ + /** + * @var string + */ + public $repositoryClass; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php new file mode 100644 index 00000000..a9d5d121 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php @@ -0,0 +1,760 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.0 + */ +class MappingException extends \Doctrine\ORM\ORMException +{ + /** + * @return MappingException + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string $entityName + * + * @return MappingException + */ + public static function identifierRequired($entityName) + { + if (false !== ($parent = get_parent_class($entityName))) { + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s" sub class of "%s". Every Entity must have an identifier/primary key.', + $entityName, $parent + )); + } + + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s". Every Entity must have an identifier/primary key.', + $entityName + )); + + } + + /** + * @param string $entityName + * @param string $type + * + * @return MappingException + */ + public static function invalidInheritanceType($entityName, $type) + { + return new self("The inheritance type '$type' specified for '$entityName' does not exist."); + } + + /** + * @return MappingException + */ + public static function generatorNotAllowedWithCompositeId() + { + return new self("Id generators can't be used with a composite id."); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function missingFieldName($entity) + { + return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingTargetEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingSourceEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return MappingException + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * Exception for invalid property name override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldName($className, $fieldName) + { + return new self("Invalid field override named '$fieldName' for class '$className'."); + } + + /** + * Exception for invalid property type override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldType($className, $fieldName) + { + return new self("The column type of attribute '$fieldName' on class '$className' could not be changed."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' on class '$className'."); + } + + /** + * @param string $className + * @param string $queryName + * + * @return MappingException + */ + public static function queryNotFound($className, $queryName) + { + return new self("No query found named '$queryName' on class '$className'."); + } + + /** + * @param string $className + * @param string $resultName + * + * @return MappingException + */ + public static function resultMappingNotFound($className, $resultName) + { + return new self("No result set mapping found named '$resultName' on class '$className'."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function emptyQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForQueryMapping($className) + { + return new self("Query name on entity class '$className' is not defined."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function missingQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingEntity($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingFieldName($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForSqlResultSetMapping($className) + { + return new self("Result set mapping name on entity class '$className' is not defined."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function oneToManyRequiresMappedBy($fieldName) + { + return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function joinTableRequired($fieldName) + { + return new self("The mapping of field '$fieldName' requires an the 'joinTable' attribute."); + } + + /** + * Called if a required option was not found but is required + * + * @param string $field Which field cannot be processed? + * @param string $expectedOption Which option is required + * @param string $hint Can optionally be used to supply a tip for common mistakes, + * e.g. "Did you think of the plural s?" + * + * @return MappingException + */ + static function missingRequiredOption($field, $expectedOption, $hint = '') + { + $message = "The mapping of field '{$field}' is invalid: The option '{$expectedOption}' is required."; + + if ( ! empty($hint)) { + $message .= ' (Hint: ' . $hint . ')'; + } + + return new self($message); + } + + /** + * Generic exception for invalid mappings. + * + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidMapping($fieldName) + { + return new self("The mapping of field '$fieldName' is invalid."); + } + + /** + * Exception for reflection exceptions - adds the entity name, + * because there might be long classnames that will be shortened + * within the stacktrace + * + * @param string $entity The entity's name + * @param \ReflectionException $previousException + * + * @return MappingException + */ + public static function reflectionFailure($entity, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $entity, 0, $previousException); + } + + /** + * @param string $className + * @param string $joinColumn + * + * @return MappingException + */ + public static function joinColumnMustPointToMappedField($className, $joinColumn) + { + return new self('The column ' . $joinColumn . ' must be mapped to a field in class ' + . $className . ' since it is referenced by a join column of another class.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function classIsNotAValidEntityOrMappedSuperClass($className) + { + if (false !== ($parent = get_parent_class($className))) { + return new self(sprintf( + 'Class "%s" sub class of "%s" is not a valid entity or mapped super class.', + $className, $parent + )); + } + + return new self(sprintf( + 'Class "%s" is not a valid entity or mapped super class.', + $className + )); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function propertyTypeIsRequired($className, $propertyName) + { + return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName."."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function tableIdGeneratorNotImplemented($className) + { + return new self("TableIdGenerator is not yet implemented for use with class ".$className); + } + + /** + * @param string $entity The entity's name. + * @param string $fieldName The name of the field that was already declared. + * + * @return MappingException + */ + public static function duplicateFieldMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $fieldName + * + * @return MappingException + */ + public static function duplicateAssociationMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function duplicateQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function duplicateResultSetMapping($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + { + return new self('Single id is not allowed on composite primary key in entity '.$entity); + } + + /** + * @param string $entity + * @param string $fieldName + * @param string $unsupportedType + * + * @return MappingException + */ + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + { + return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' + .'is not supported by Doctrine.' + ); + } + + /** + * @param string|null $path + * + * @return MappingException + */ + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + /** + * Returns an exception that indicates that a class used in a discriminator map does not exist. + * An example would be an outdated (maybe renamed) classname. + * + * @param string $className The class that could not be found + * @param string $owningClass The class that declares the discriminator map. + * + * @return MappingException + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { + return new self( + "Entity class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + /** + * @param string $className + * @param array $entries + * @param array $map + * + * @return MappingException + */ + public static function duplicateDiscriminatorEntry($className, array $entries, array $map) + { + return new self( + "The entries " . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " . + "If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. " . + "The entries of the current map are: @DiscriminatorMap({" . implode(', ', array_map( + function($a, $b) { return "'$a': '$b'"; }, array_keys($map), array_values($map) + )) . "})" + ); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorMap($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator map was defined."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorColumn($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); + } + + /** + * @param string $className + * @param string $type + * + * @return MappingException + */ + public static function invalidDiscriminatorColumnType($className, $type) + { + return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!"); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForDiscriminatorColumns($className) + { + return new self("Discriminator column name on entity class '$className' is not defined."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionable in entity class '$className' is not supported."); + } + + /** + * @param string $className + * @param string $fieldName + * @param string $type + * + * @return MappingException + */ + public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type) + { + return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers."); + } + + /** + * @param string $className + * @param string $columnName + * + * @return MappingException + */ + public static function duplicateColumnName($className, $columnName) + { + return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyAssociationOnMappedSuperclass($className, $field) + { + return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'."); + } + + /** + * @param string $className + * @param string $targetEntity + * @param string $targetField + * + * @return MappingException + */ + public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField) + { + return new self("It is not possible to map entity '".$className."' with a composite primary key ". + "as part of the primary key of another entity '".$targetEntity."#".$targetField."'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function noSingleAssociationJoinColumnFound($className, $field) + { + return new self("'$className#$field' is not an association with a single join column."); + } + + /** + * @param string $className + * @param string $column + * + * @return MappingException + */ + public static function noFieldNameFoundForColumn($className, $column) + { + return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ". + "field does not exist or an association exists but it has multiple join columns."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field) + { + return new self("The orphan removal option is not allowed on an association that is ". + "part of the identifier in '$className#$field'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalOrphanRemoval($className, $field) + { + return new self("Orphan removal is only allowed on one-to-one and one-to-many ". + "associations, but " . $className."#" .$field . " is not."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalInverseIdentifierAssociation($className, $field) + { + return new self("An inverse association is not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyIdentifierAssociation($className, $field) + { + return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function noInheritanceOnMappedSuperClass($className) + { + return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'."); + } + + /** + * @param string $className + * @param string $rootClassName + * + * @return MappingException + */ + public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName) + { + return new self( + "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " . + "to be properly mapped in the inheritance hierarchy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occurring." + ); + } + + /** + * @param string $className + * @param string $methodName + * + * @return MappingException + */ + public static function lifecycleCallbackMethodNotFound($className, $methodName) + { + return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); + } + + /** + * @param string $listenerName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerClassNotFound($listenerName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" not found.', $listenerName, $className)); + } + + /** + * @param string $listenerName + * @param string $methodName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerMethodNotFound($listenerName, $methodName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName)); + } + + /** + * @param string $className + * @param string $annotation + * + * @return MappingException + */ + public static function invalidFetchMode($className, $annotation) + { + return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'"); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function compositeKeyAssignedIdGeneratorRequired($className) + { + return new self("Entity '". $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported."); + } + + /** + * @param string $targetEntity + * @param string $sourceEntity + * @param string $associationName + * + * @return MappingException + */ + public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + { + return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'."); + } + + /** + * @param array $cascades + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function invalidCascadeOption(array $cascades, $className, $propertyName) + { + $cascades = implode(", ", array_map(function ($e) { return "'" . $e . "'"; }, $cascades)); + return new self(sprintf( + "You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'", + $className, + $propertyName, + $cascades + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php new file mode 100644 index 00000000..7f5460a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of native SQL named queries. + * The NamedNativeQueries annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class NamedNativeQueries implements Annotation +{ + /** + * One or more NamedNativeQuery annotations. + * + * @var array<\Doctrine\ORM\Mapping\NamedNativeQuery> + */ + public $value = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php new file mode 100644 index 00000000..f336c991 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify a native SQL named query. + * The NamedNativeQuery annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedNativeQuery implements Annotation +{ + /** + * The name used to refer to the query with the EntityManager methods that create query objects. + * + * @var string + */ + public $name; + + /** + * The SQL query string. + * + * @var string + */ + public $query; + + /** + * The class of the result. + * + * @var string + */ + public $resultClass; + + /** + * The name of a SqlResultSetMapping, as defined in metadata. + * + * @var string + */ + public $resultSetMapping; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php new file mode 100644 index 00000000..5fce0727 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class NamedQueries implements Annotation +{ + /** + * @var array<\Doctrine\ORM\Mapping\NamedQuery> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php new file mode 100644 index 00000000..c4e6cd52 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedQuery implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $query; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php new file mode 100644 index 00000000..fc66905c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A set of rules for determining the physical column and table names + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +interface NamingStrategy +{ + /** + * Returns a table name for an entity class. + * + * @param string $className The fully-qualified class name. + * + * @return string A table name. + */ + function classToTableName($className); + + /** + * Returns a column name for a property. + * + * @param string $propertyName A property name. + * @param string|null $className The fully-qualified class name. + * + * @return string A column name. + */ + function propertyToColumnName($propertyName, $className = null); + + /** + * Returns the default reference column name. + * + * @return string A column name. + */ + function referenceColumnName(); + + /** + * Returns a join column name for a property. + * + * @param string $propertyName A property name. + * + * @return string A join column name. + */ + function joinColumnName($propertyName); + + /** + * Returns a join table name. + * + * @param string $sourceEntity The source entity. + * @param string $targetEntity The target entity. + * @param string|null $propertyName A property name. + * + * @return string A join table name. + */ + function joinTableName($sourceEntity, $targetEntity, $propertyName = null); + + /** + * Returns the foreign key column name for the given parameters. + * + * @param string $entityName An entity. + * @param string|null $referencedColumnName A property. + * + * @return string A join column name. + */ + function joinKeyColumnName($entityName, $referencedColumnName = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php new file mode 100644 index 00000000..4b246571 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToMany implements Annotation +{ + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; + + /** + * @var string + */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php new file mode 100644 index 00000000..b2ab81f8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToOne implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $inversedBy; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php new file mode 100644 index 00000000..ad1b7a8f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OrderBy implements Annotation +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php new file mode 100644 index 00000000..2f8e9932 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostLoad implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php new file mode 100644 index 00000000..2aea7194 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostPersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php new file mode 100644 index 00000000..321c4bd5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php new file mode 100644 index 00000000..a55f7072 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php new file mode 100644 index 00000000..6697d372 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreFlush implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php new file mode 100644 index 00000000..fea05be6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PrePersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php new file mode 100644 index 00000000..29822eda --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php new file mode 100644 index 00000000..290df72e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php new file mode 100644 index 00000000..2ad7e7c9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the column, alias and table quotes. + * + * @since 2.3 + * @author Fabio B. Silva + */ +interface QuoteStrategy +{ + /** + * Gets the (possibly quoted) column name for safe use in an SQL statement. + * + * @param string $fieldName + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) primary table name for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getTableName(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) sequence name for safe use in an SQL statement. + * + * @param array $definition + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) name of the join table. + * + * @param array $association + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return array + */ + function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the column alias. + * + * @param string $columnName + * @param integer $counter + * @param AbstractPlatform $platform + * @param ClassMetadata|null $class + * + * @return string + */ + function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null); + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php new file mode 100644 index 00000000..ba1c45b6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class SequenceGenerator implements Annotation +{ + /** + * @var string + */ + public $sequenceName; + + /** + * @var integer + */ + public $allocationSize = 1; + + /** + * @var integer + */ + public $initialValue = 1; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php new file mode 100644 index 00000000..f5ead7c0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The SqlResultSetMapping annotation is used to specify the mapping of the result of a native SQL query. + * The SqlResultSetMapping annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class SqlResultSetMapping implements Annotation +{ + /** + * The name given to the result set mapping, and used to refer to it in the methods of the Query API. + * + * @var string + */ + public $name; + + /** + * Specifies the result set mapping to entities. + * + * @var array<\Doctrine\ORM\Mapping\EntityResult> + */ + public $entities = array(); + + /** + * Specifies the result set mapping to scalar values. + * + * @var array<\Doctrine\ORM\Mapping\ColumnResult> + */ + public $columns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php new file mode 100644 index 00000000..c21b2ab8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of mappings. + * The SqlResultSetMappings annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class SqlResultSetMappings implements Annotation +{ + /** + * One or more SqlResultSetMapping annotations. + * + * @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping> + */ + public $value = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php new file mode 100644 index 00000000..f9f8d4a6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Table implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $schema; + + /** + * @var array<\Doctrine\ORM\Mapping\Index> + */ + public $indexes; + + /** + * @var array<\Doctrine\ORM\Mapping\UniqueConstraint> + */ + public $uniqueConstraints; + + /** + * @var array + */ + public $options = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php new file mode 100644 index 00000000..5231aaaf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -0,0 +1,138 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Naming strategy implementing the underscore naming convention. + * Converts 'MyEntity' to 'my_entity' or 'MY_ENTITY'. + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class UnderscoreNamingStrategy implements NamingStrategy +{ + /** + * @var integer + */ + private $case; + + /** + * Underscore naming strategy construct. + * + * @param integer $case CASE_LOWER | CASE_UPPER + */ + public function __construct($case = CASE_LOWER) + { + $this->case = $case; + } + + /** + * @return integer CASE_LOWER | CASE_UPPER + */ + public function getCase() + { + return $this->case; + } + + /** + * Sets string case CASE_LOWER | CASE_UPPER. + * Alphabetic characters converted to lowercase or uppercase. + * + * @param integer $case + * + * @return void + */ + public function setCase($case) + { + $this->case = $case; + } + + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, '\\') + 1); + } + + return $this->underscore($className); + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $this->underscore($propertyName); + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return $this->case === CASE_UPPER ? 'ID' : 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName) + { + return $this->underscore($propertyName) . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return $this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName()); + } + + /** + * @param string $string + * + * @return string + */ + private function underscore($string) + { + $string = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $string); + + if ($this->case === CASE_UPPER) { + return strtoupper($string); + } + + return strtolower($string); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php new file mode 100644 index 00000000..95d99293 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class UniqueConstraint implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php new file mode 100644 index 00000000..a2377027 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Version implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php new file mode 100644 index 00000000..b19f8180 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Represents a native SQL query. + * + * @author Roman Borschel + * @since 2.0 + */ +final class NativeQuery extends AbstractQuery +{ + /** + * @var string + */ + private $_sql; + + /** + * Sets the SQL of the query. + * + * @param string $sql + * + * @return NativeQuery This query instance. + */ + public function setSQL($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * Gets the SQL query. + * + * @return mixed The built SQL query or an array of all SQL queries. + * + * @override + */ + public function getSQL() + { + return $this->_sql; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $parameters = array(); + $types = array(); + + foreach ($this->getParameters() as $parameter) { + $name = $parameter->getName(); + $value = $this->processParameterValue($parameter->getValue()); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : Query\ParameterTypeInferer::inferType($value); + + $parameters[$name] = $value; + $types[$name] = $type; + } + + if ($parameters && is_int(key($parameters))) { + ksort($parameters); + ksort($types); + + $parameters = array_values($parameters); + $types = array_values($types); + } + + return $this->_em->getConnection()->executeQuery( + $this->_sql, $parameters, $types, $this->_queryCacheProfile + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php new file mode 100644 index 00000000..2cbac8e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly does not return any results. + * + * @author robo + * @since 2.0 + */ +class NoResultException extends UnexpectedResultException +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct('No result was found for query although at least one row was expected.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php new file mode 100644 index 00000000..55b71300 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly returns more than one result. + * + * @author robo + * @since 2.0 + */ +class NonUniqueResultException extends UnexpectedResultException +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php new file mode 100644 index 00000000..99333f03 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php @@ -0,0 +1,271 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; + +/** + * Base exception class for all ORM exceptions. + * + * @author Roman Borschel + * @since 2.0 + */ +class ORMException extends Exception +{ + /** + * @return ORMException + */ + public static function missingMappingDriverImpl() + { + return new self("It's a requirement to specify a Metadata Driver and pass it ". + "to Doctrine\\ORM\\Configuration::setMetadataDriverImpl()."); + } + + /** + * @param string $queryName + * + * @return ORMException + */ + public static function namedQueryNotFound($queryName) + { + return new self('Could not find a named query by the name "' . $queryName . '"'); + } + + /** + * @param string $nativeQueryName + * + * @return ORMException + */ + public static function namedNativeQueryNotFound($nativeQueryName) + { + return new self('Could not find a named native query by the name "' . $nativeQueryName . '"'); + } + + /** + * @param object $entity + * @param object $relatedEntity + * + * @return ORMException + */ + public static function entityMissingForeignAssignedId($entity, $relatedEntity) + { + return new self( + "Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " . + "however this entity has no identity itself. You have to call EntityManager#persist() on the related entity " . + "and make sure that an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " . + "of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call " . + "EntityManager#flush() between both persist operations." + ); + } + + /** + * @param object $entity + * @param string $field + * + * @return ORMException + */ + public static function entityMissingAssignedIdForField($entity, $field) + { + return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " . + "The identifier generation strategy for this entity requires the ID field to be populated before ". + "EntityManager#persist() is called. If you want automatically generated identifiers instead " . + "you need to adjust the metadata mapping accordingly." + ); + } + + /** + * @param string $field + * + * @return ORMException + */ + public static function unrecognizedField($field) + { + return new self("Unrecognized field: $field"); + } + + /** + * @param string $className + * @param string $field + * + * @return ORMException + */ + public static function invalidOrientation($className, $field) + { + return new self("Invalid order by orientation specified for " . $className . "#" . $field); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidFlushMode($mode) + { + return new self("'$mode' is an invalid flush mode."); + } + + /** + * @return ORMException + */ + public static function entityManagerClosed() + { + return new self("The EntityManager is closed."); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidHydrationMode($mode) + { + return new self("'$mode' is an invalid hydration mode."); + } + + /** + * @return ORMException + */ + public static function mismatchedEventManager() + { + return new self("Cannot use different EventManager instances for EntityManager and Connection."); + } + + /** + * @param string $methodName + * + * @return ORMException + */ + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + /** + * @param string $entityName + * @param string $fieldName + * @param string $method + * + * @return ORMException + */ + public static function invalidFindByCall($entityName, $fieldName, $method) + { + return new self( + "Entity '".$entityName."' has no field '".$fieldName."'. ". + "You can therefore not call '".$method."' on the entities' repository" + ); + } + + /** + * @param string $entityName + * @param string $associationFieldName + * + * @return ORMException + */ + public static function invalidFindByInverseAssociation($entityName, $associationFieldName) + { + return new self( + "You cannot search for the association field '".$entityName."#".$associationFieldName."', ". + "because it is the inverse side of an association. Find methods only work on owning side associations." + ); + } + + /** + * @return ORMException + */ + public static function invalidResultCacheDriver() + { + return new self("Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache."); + } + + /** + * @return ORMException + */ + public static function notSupported() + { + return new self("This behaviour is (currently) not supported by Doctrine 2"); + } + + /** + * @return ORMException + */ + public static function queryCacheNotConfigured() + { + return new self('Query Cache is not configured.'); + } + + /** + * @return ORMException + */ + public static function metadataCacheNotConfigured() + { + return new self('Class Metadata Cache is not configured.'); + } + + /** + * @return ORMException + */ + public static function proxyClassesAlwaysRegenerating() + { + return new self('Proxy Classes are always regenerating.'); + } + + /** + * @param string $entityNamespaceAlias + * + * @return ORMException + */ + public static function unknownEntityNamespace($entityNamespaceAlias) + { + return new self( + "Unknown Entity namespace alias '$entityNamespaceAlias'." + ); + } + + /** + * @param string $className + * + * @return ORMException + */ + public static function invalidEntityRepository($className) + { + return new self("Invalid repository class '".$className."'. It must be a Doctrine\Common\Persistence\ObjectRepository."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return ORMException + */ + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } + + /** + * @param string $functionName + * + * @return ORMException + */ + public static function overwriteInternalDQLFunctionNotAllowed($functionName) + { + return new self("It is not allowed to overwrite internal function '$functionName' in the DQL parser through user-defined functions."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php new file mode 100644 index 00000000..cb503767 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -0,0 +1,201 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork + * + * @author Benjamin Eberlei + */ +class ORMInvalidArgumentException extends \InvalidArgumentException +{ + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForManagedEntity($entity) + { + return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForRemovedEntity($entity) + { + return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertTwice($entity) + { + return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice."); + } + + /** + * @param string $className + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityWithoutIdentity($className, $entity) + { + return new self( + "The given entity of type '" . $className . "' (".self::objToStr($entity).") has no identity/no " . + "id values set. It cannot be added to the identity map." + ); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function readOnlyRequiresManagedEntity($entity) + { + return new self("Only managed entities can be marked or checked as read only. But " . self::objToStr($entity) . " is not"); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + static public function newEntityFoundThroughRelationship(array $assoc, $entry) + { + return new self("A new entity was found through the relationship '" + . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not" + . " configured to cascade persist operations for entity: " . self::objToStr($entry) . "." + . " To solve this issue: Either explicitly call EntityManager#persist()" + . " on this unknown entity or configure cascade persist " + . " this association in the mapping for example @ManyToOne(..,cascade={\"persist\"})." + . (method_exists($entry, '__toString') ? + "": + " If you cannot find out which entity causes the problem" + ." implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.")); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityFoundThroughRelationship(array $assoc, $entry) + { + return new self("A detached entity of type " . $assoc['targetEntity'] . " (" . self::objToStr($entry) . ") " + . " was found through the relationship '" . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' " + . "during cascading a persist operation."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityNotManaged($entity) + { + return new self("Entity " . self::objToStr($entity) . " is not managed. An entity is managed if its fetched " . + "from the database or registered as new through EntityManager#persist"); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityHasNoIdentity($entity, $operation) + { + return new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityIsRemoved($entity, $operation) + { + return new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityCannot($entity, $operation) + { + return new self("A detached entity was found during " . $operation . " " . self::objToStr($entity)); + } + + /** + * @param string $context + * @param mixed $given + * @param int $parameterIndex + * + * @return ORMInvalidArgumentException + */ + public static function invalidObject($context, $given, $parameterIndex = 1) + { + return new self($context . ' expects parameter ' . $parameterIndex . + ' to be an entity object, '. gettype($given) . ' given.'); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidCompositeIdentifier() + { + return new self("Binding an entity with a composite primary key to a query is not supported. " . + "You should split the parameter into the explicit fields and bind them separately."); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidIdentifierBindingEntity() + { + return new self("Binding entities to query parameters only allowed for entities that have an identifier."); + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php new file mode 100644 index 00000000..57ea66bd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * An OptimisticLockException is thrown when a version check on an object + * that uses optimistic locking through a version field fails. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class OptimisticLockException extends ORMException +{ + /** + * @var object|null + */ + private $entity; + + /** + * @param string $msg + * @param object $entity + */ + public function __construct($msg, $entity) + { + parent::__construct($msg); + $this->entity = $entity; + } + + /** + * Gets the entity that caused the exception. + * + * @return object|null + */ + public function getEntity() + { + return $this->entity; + } + + /** + * @param object $entity + * + * @return OptimisticLockException + */ + public static function lockFailed($entity) + { + return new self("The optimistic lock on an entity failed.", $entity); + } + + /** + * @param object $entity + * @param int $expectedLockVersion + * @param int $actualLockVersion + * + * @return OptimisticLockException + */ + public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) + { + return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); + } + + /** + * @param string $entityName + * + * @return OptimisticLockException + */ + public static function notVersioned($entityName) + { + return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php new file mode 100644 index 00000000..53ba1c1f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php @@ -0,0 +1,864 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; + +use Closure; + +/** + * A PersistentCollection represents a collection of elements that have persistent state. + * + * Collections of entities represent only the associations (links) to those entities. + * That means, if the collection is part of a many-many mapping and you remove + * entities from the collection, only the links in the relation table are removed (on flush). + * Similarly, if you remove entities from a collection that is part of a one-many + * mapping this will only result in the nulling out of the foreign keys on flush. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Giorgio Sironi + * @author Stefano Rodriguez + * @todo Design for inheritance to allow custom implementations? + */ +final class PersistentCollection implements Collection, Selectable +{ + /** + * A snapshot of the collection at the moment it was fetched from the database. + * This is used to create a diff of the collection at commit time. + * + * @var array + */ + private $snapshot = array(); + + /** + * The entity that owns this collection. + * + * @var object + */ + private $owner; + + /** + * The association mapping the collection belongs to. + * This is currently either a OneToManyMapping or a ManyToManyMapping. + * + * @var array + */ + private $association; + + /** + * The EntityManager that manages the persistence of the collection. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The name of the field on the target entities that points to the owner + * of the collection. This is only set if the association is bi-directional. + * + * @var string + */ + private $backRefFieldName; + + /** + * The class descriptor of the collection's entity type. + * + * @var ClassMetadata + */ + private $typeClass; + + /** + * Whether the collection is dirty and needs to be synchronized with the database + * when the UnitOfWork that manages its persistent state commits. + * + * @var boolean + */ + private $isDirty = false; + + /** + * Whether the collection has already been initialized. + * + * @var boolean + */ + private $initialized = true; + + /** + * The wrapped Collection instance. + * + * @var Collection + */ + private $coll; + + /** + * Creates a new persistent collection. + * + * @param EntityManager $em The EntityManager the collection will be associated with. + * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param array $coll The collection elements. + */ + public function __construct(EntityManager $em, $class, $coll) + { + $this->coll = $coll; + $this->em = $em; + $this->typeClass = $class; + } + + /** + * INTERNAL: + * Sets the collection's owning entity together with the AssociationMapping that + * describes the association between the owner and the elements of the collection. + * + * @param object $entity + * @param array $assoc + * + * @return void + */ + public function setOwner($entity, array $assoc) + { + $this->owner = $entity; + $this->association = $assoc; + $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; + } + + /** + * INTERNAL: + * Gets the collection owner. + * + * @return object + */ + public function getOwner() + { + return $this->owner; + } + + /** + * @return Mapping\ClassMetadata + */ + public function getTypeClass() + { + return $this->typeClass; + } + + /** + * INTERNAL: + * Adds an element to a collection during hydration. This will automatically + * complete bidirectional associations in the case of a one-to-many association. + * + * @param mixed $element The element to add. + * + * @return void + */ + public function hydrateAdd($element) + { + $this->coll->add($element); + + // If _backRefFieldName is set and its a one-to-many association, + // we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + + $this->em->getUnitOfWork()->setOriginalEntityProperty( + spl_object_hash($element), $this->backRefFieldName, $this->owner + ); + } + } + + /** + * INTERNAL: + * Sets a keyed element in the collection during hydration. + * + * @param mixed $key The key to set. + * @param mixed $element The element to set. + * + * @return void + */ + public function hydrateSet($key, $element) + { + $this->coll->set($key, $element); + + // If _backRefFieldName is set, then the association is bidirectional + // and we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + } + } + + /** + * Initializes the collection by loading its contents from the database + * if the collection is not yet initialized. + * + * @return void + */ + public function initialize() + { + if ($this->initialized || ! $this->association) { + return; + } + + // Has NEW objects added through add(). Remember them. + $newObjects = array(); + + if ($this->isDirty) { + $newObjects = $this->coll->toArray(); + } + + $this->coll->clear(); + $this->em->getUnitOfWork()->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if ($newObjects) { + foreach ($newObjects as $obj) { + $this->coll->add($obj); + } + + $this->isDirty = true; + } + + $this->initialized = true; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + * + * @return void + */ + public function takeSnapshot() + { + $this->snapshot = $this->coll->toArray(); + $this->isDirty = false; + } + + /** + * INTERNAL: + * Returns the last snapshot of the elements in the collection. + * + * @return array The last snapshot of the elements. + */ + public function getSnapshot() + { + return $this->snapshot; + } + + /** + * INTERNAL: + * getDeleteDiff + * + * @return array + */ + public function getDeleteDiff() + { + return array_udiff_assoc( + $this->snapshot, + $this->coll->toArray(), + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc( + $this->coll->toArray(), + $this->snapshot, + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: Gets the association mapping of the collection. + * + * @return array + */ + public function getMapping() + { + return $this->association; + } + + /** + * Marks this collection as changed/dirty. + * + * @return void + */ + private function changed() + { + if ($this->isDirty) { + return; + } + + $this->isDirty = true; + + if ($this->association !== null && + $this->association['isOwningSide'] && + $this->association['type'] === ClassMetadata::MANY_TO_MANY && + $this->owner && + $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { + $this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner); + } + } + + /** + * Gets a boolean flag indicating whether this collection is dirty which means + * its state needs to be synchronized with the database. + * + * @return boolean TRUE if the collection is dirty, FALSE otherwise. + */ + public function isDirty() + { + return $this->isDirty; + } + + /** + * Sets a boolean flag, indicating whether this collection is dirty. + * + * @param boolean $dirty Whether the collection should be marked dirty or not. + * + * @return void + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + * + * @return void + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * Checks whether this collection has been initialized. + * + * @return boolean + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * {@inheritdoc} + */ + public function first() + { + $this->initialize(); + + return $this->coll->first(); + } + + /** + * {@inheritdoc} + */ + public function last() + { + $this->initialize(); + + return $this->coll->last(); + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + // TODO: If the keys are persistent as well (not yet implemented) + // and the collection is not initialized and orphanRemoval is + // not used we can issue a straight SQL delete/update on the + // association (table). Without initializing the collection. + $this->initialize(); + + $removed = $this->coll->remove($key); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function removeElement($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + if ($this->coll->contains($element)) { + return $this->coll->removeElement($element); + } + + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + if ($persister->removeElement($this, $element)) { + return $element; + } + + return null; + } + + $this->initialize(); + + $removed = $this->coll->removeElement($element); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function containsKey($key) + { + $this->initialize(); + + return $this->coll->containsKey($key); + } + + /** + * {@inheritdoc} + */ + public function contains($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $this->coll->contains($element) || $persister->contains($this, $element); + } + + $this->initialize(); + + return $this->coll->contains($element); + } + + /** + * {@inheritdoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + + return $this->coll->exists($p); + } + + /** + * {@inheritdoc} + */ + public function indexOf($element) + { + $this->initialize(); + + return $this->coll->indexOf($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->initialize(); + + return $this->coll->get($key); + } + + /** + * {@inheritdoc} + */ + public function getKeys() + { + $this->initialize(); + + return $this->coll->getKeys(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + $this->initialize(); + + return $this->coll->getValues(); + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0); + } + + $this->initialize(); + + return $this->coll->count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + $this->initialize(); + + $this->coll->set($key, $value); + + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->coll->add($value); + + $this->changed(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + $this->initialize(); + + return $this->coll->isEmpty(); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $this->initialize(); + + return $this->coll->getIterator(); + } + + /** + * {@inheritdoc} + */ + public function map(Closure $func) + { + $this->initialize(); + + return $this->coll->map($func); + } + + /** + * {@inheritdoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + + return $this->coll->filter($p); + } + + /** + * {@inheritdoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + + return $this->coll->forAll($p); + } + + /** + * {@inheritdoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + + return $this->coll->partition($p); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $this->initialize(); + + return $this->coll->toArray(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + if ($this->initialized && $this->isEmpty()) { + return; + } + + $uow = $this->em->getUnitOfWork(); + + if ($this->association['type'] & ClassMetadata::TO_MANY && + $this->association['orphanRemoval'] && + $this->owner) { + // we need to initialize here, as orphan removal acts like implicit cascadeRemove, + // hence for event listeners we need the objects in memory. + $this->initialize(); + + foreach ($this->coll as $element) { + $uow->scheduleOrphanRemoval($element); + } + } + + $this->coll->clear(); + + $this->initialized = true; // direct call, {@link initialize()} is too expensive + + if ($this->association['isOwningSide'] && $this->owner) { + $this->changed(); + + $uow->scheduleCollectionDeletion($this); + + $this->takeSnapshot(); + } + } + + /** + * Called by PHP when this collection is serialized. Ensures that only the + * elements are properly serialized. + * + * @return array + * + * @internal Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + */ + public function __sleep() + { + return array('coll', 'initialized'); + } + + /* ArrayAccess implementation */ + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + return $this->set($offset, $value); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->coll->key(); + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->coll->current(); + } + + /** + * {@inheritdoc} + */ + public function next() + { + return $this->coll->next(); + } + + /** + * Retrieves the wrapped Collection instance. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function unwrap() + { + return $this->coll; + } + + /** + * 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 + * @param int|null $length + * + * @return array + */ + public function slice($offset, $length = null) + { + if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->slice($this, $offset, $length); + } + + $this->initialize(); + + return $this->coll->slice($offset, $length); + } + + /** + * Cleans up internal state of cloned persistent collection. + * + * The following problems have to be prevented: + * 1. Added entities are added to old PC + * 2. New collection is not dirty, if reused on other entity nothing + * changes. + * 3. Snapshot leads to invalid diffs being generated. + * 4. Lazy loading grabs entities from old owner object. + * 5. New collection is connected to old owner and leads to duplicate keys. + * + * @return void + */ + public function __clone() + { + if (is_object($this->coll)) { + $this->coll = clone $this->coll; + } + + $this->initialize(); + + $this->owner = null; + $this->snapshot = array(); + + $this->changed(); + } + + /** + * Selects all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return Collection + * + * @throws \RuntimeException + */ + public function matching(Criteria $criteria) + { + if ($this->isDirty) { + $this->initialize(); + } + + if ($this->initialized) { + return $this->coll->matching($criteria); + } + + if ($this->association['type'] !== ClassMetadata::ONE_TO_MANY) { + throw new \RuntimeException("Matching Criteria on PersistentCollection only works on OneToMany associations at the moment."); + } + + $builder = Criteria::expr(); + $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); + $expression = $criteria->getWhereExpression(); + $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; + + $criteria->where($expression); + + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']); + + return new ArrayCollection($persister->loadCriteria($criteria)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php new file mode 100644 index 00000000..fdc54aee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -0,0 +1,322 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\PersistentCollection; + +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ +abstract class AbstractCollectionPersister +{ + /** + * @var EntityManager + */ + protected $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $uow; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * Initializes a new instance of a class derived from AbstractCollectionPersister. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Deletes the persistent state represented by the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return void + */ + public function delete(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $sql = $this->getDeleteSQL($coll); + + $this->conn->executeUpdate($sql, $this->getDeleteSQLParameters($coll)); + } + + /** + * Gets the SQL statement for deleting the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + abstract protected function getDeleteSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return array + */ + abstract protected function getDeleteSQLParameters(PersistentCollection $coll); + + /** + * Updates the given collection, synchronizing its state with the database + * by inserting, updating and deleting individual elements. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return void + */ + public function update(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $this->deleteRows($coll); + $this->insertRows($coll); + } + + /** + * Deletes rows. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return void + */ + public function deleteRows(PersistentCollection $coll) + { + $diff = $coll->getDeleteDiff(); + $sql = $this->getDeleteRowSQL($coll); + + foreach ($diff as $element) { + $this->conn->executeUpdate($sql, $this->getDeleteRowSQLParameters($coll, $element)); + } + } + + /** + * Inserts rows. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return void + */ + public function insertRows(PersistentCollection $coll) + { + $diff = $coll->getInsertDiff(); + $sql = $this->getInsertRowSQL($coll); + + foreach ($diff as $element) { + $this->conn->executeUpdate($sql, $this->getInsertRowSQLParameters($coll, $element)); + } + } + + /** + * Counts the size of this persistent collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return integer + * + * @throws \BadMethodCallException + */ + public function count(PersistentCollection $coll) + { + throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister."); + } + + /** + * Slices elements. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param integer $offset + * @param integer $length + * + * @return array + * + * @throws \BadMethodCallException + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister."); + } + + /** + * Checks for existence of an element. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return boolean + * + * @throws \BadMethodCallException + */ + public function contains(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Checking for existence of an element is not supported by this CollectionPersister."); + } + + /** + * Checks for existence of a key. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $key + * + * @return boolean + * + * @throws \BadMethodCallException + */ + public function containsKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Checking for existence of a key is not supported by this CollectionPersister."); + } + + /** + * Removes an element. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return mixed + * + * @throws \BadMethodCallException + */ + public function removeElement(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister."); + } + + /** + * Removes an element by key. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $key + * + * @return void + * + * @throws \BadMethodCallException + */ + public function removeKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister."); + } + + /** + * Gets an element by key. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $index + * + * @return mixed + * + * @throws \BadMethodCallException + */ + public function get(PersistentCollection $coll, $index) + { + throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister."); + } + + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + abstract protected function getDeleteRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $element + * + * @return array + */ + abstract protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element); + + /** + * Gets the SQL statement used for updating a row in the collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + abstract protected function getUpdateRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + */ + abstract protected function getInsertRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param mixed $element + * + * @return array + */ + abstract protected function getInsertRowSQLParameters(PersistentCollection $coll, $element); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php new file mode 100644 index 00000000..42224957 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Types\Type; + +/** + * Base class for entity persisters that implement a certain inheritance mapping strategy. + * All these persisters are assumed to use a discriminator column to discriminate entity + * types in the hierarchy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractEntityInheritancePersister extends BasicEntityPersister +{ + /** + * {@inheritdoc} + */ + protected function prepareInsertData($entity) + { + $data = parent::prepareInsertData($entity); + + // Populate the discriminator column + $discColumn = $this->class->discriminatorColumn; + $this->columnTypes[$discColumn['name']] = $discColumn['type']; + $data[$this->getDiscriminatorColumnTableName()][$discColumn['name']] = $this->class->discriminatorValue; + + return $data; + } + + /** + * Gets the name of the table that contains the discriminator column. + * + * @return string The table name. + */ + abstract protected function getDiscriminatorColumnTableName(); + + /** + * {@inheritdoc} + */ + protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $tableAlias = $alias == 'r' ? '' : $alias; + $columnName = $class->columnNames[$field]; + $columnAlias = $this->getSQLColumnAlias($columnName); + $sql = $this->getSQLTableAlias($class->name, $tableAlias) . '.' + . $this->quoteStrategy->getColumnName($field, $class, $this->platform); + + $this->rsm->addFieldResult($alias, $columnAlias, $field, $class->name); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + /** + * @param string $tableAlias + * @param string $joinColumnName + * @param string $className + * + * @return string + */ + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) + { + $columnAlias = $this->getSQLColumnAlias($joinColumnName); + $this->rsm->addMetaResult('r', $columnAlias, $joinColumnName); + + return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php new file mode 100644 index 00000000..f101fda1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -0,0 +1,1971 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Connection; + +use Doctrine\ORM\ORMException; +use Doctrine\ORM\OptimisticLockException; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Query; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Expr\Comparison; + +/** + * A BasicEntityPersister maps an entity to a single table in a relational database. + * + * A persister is always responsible for a single entity type. + * + * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent + * state of entities onto a relational database when the UnitOfWork is committed, + * as well as for basic querying of entities and their associations (not DQL). + * + * The persisting operations that are invoked during a commit of a UnitOfWork to + * persist the persistent entity state are: + * + * - {@link addInsert} : To schedule an entity for insertion. + * - {@link executeInserts} : To execute all scheduled insertions. + * - {@link update} : To update the persistent state of an entity. + * - {@link delete} : To delete the persistent state of an entity. + * + * As can be seen from the above list, insertions are batched and executed all at once + * for increased efficiency. + * + * The querying operations invoked during a UnitOfWork, either through direct find + * requests or lazy-loading, are the following: + * + * - {@link load} : Loads (the state of) a single, managed entity. + * - {@link loadAll} : Loads multiple, managed entities. + * - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading). + * - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading). + * - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading). + * + * The BasicEntityPersister implementation provides the default behavior for + * persisting and querying entities that are mapped to a single database table. + * + * Subclasses can be created to provide custom persisting and querying strategies, + * i.e. spanning multiple tables. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Benjamin Eberlei + * @author Alexander + * @author Fabio B. Silva + * @since 2.0 + */ +class BasicEntityPersister +{ + /** + * @var array + */ + static private $comparisonMap = array( + Comparison::EQ => '= %s', + Comparison::IS => '= %s', + Comparison::NEQ => '!= %s', + Comparison::GT => '> %s', + Comparison::GTE => '>= %s', + Comparison::LT => '< %s', + Comparison::LTE => '<= %s', + Comparison::IN => 'IN (%s)', + Comparison::NIN => 'NOT IN (%s)', + Comparison::CONTAINS => 'LIKE %s', + ); + + /** + * Metadata object that describes the mapping of the mapped entity class. + * + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $class; + + /** + * The underlying DBAL Connection of the used EntityManager. + * + * @var \Doctrine\DBAL\Connection $conn + */ + protected $conn; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The EntityManager instance. + * + * @var \Doctrine\ORM\EntityManager + */ + protected $em; + + /** + * Queued inserts. + * + * @var array + */ + protected $queuedInserts = array(); + + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + * + * TODO: Evaluate Caching in combination with the other cached SQL snippets. + * + * @var Query\ResultSetMapping + */ + protected $rsm; + + /** + * The map of column names to DBAL mapping types of all prepared columns used + * when INSERTing or UPDATEing an entity. + * + * @var array + * + * @see prepareInsertData($entity) + * @see prepareUpdateData($entity) + */ + protected $columnTypes = array(); + + /** + * The map of quoted column names. + * + * @var array + * + * @see prepareInsertData($entity) + * @see prepareUpdateData($entity) + */ + protected $quotedColumns = array(); + + /** + * The INSERT SQL statement used for entities handled by this persister. + * This SQL is only generated once per request, if at all. + * + * @var string + */ + private $insertSql; + + /** + * The SELECT column list SQL fragment used for querying entities by this persister. + * This SQL fragment is only generated once per request, if at all. + * + * @var string + */ + protected $selectColumnListSql; + + /** + * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. + * + * @var string + */ + protected $selectJoinSql; + + /** + * Counter for creating unique SQL table and column aliases. + * + * @var integer + */ + protected $sqlAliasCounter = 0; + + /** + * Map from class names (FQCN) to the corresponding generated SQL table aliases. + * + * @var array + */ + protected $sqlTableAliases = array(); + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * Initializes a new BasicEntityPersister that uses the given EntityManager + * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param \Doctrine\ORM\EntityManager $em + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + public function __construct(EntityManager $em, ClassMetadata $class) + { + $this->em = $em; + $this->class = $class; + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata() + { + return $this->class; + } + + /** + * Adds an entity to the queued insertions. + * The entity remains queued until {@link executeInserts} is invoked. + * + * @param object $entity The entity to queue for insertion. + * + * @return void + */ + public function addInsert($entity) + { + $this->queuedInserts[spl_object_hash($entity)] = $entity; + } + + /** + * Executes all queued entity insertions and returns any generated post-insert + * identifiers that were created as a result of the insertions. + * + * If no inserts are queued, invoking this method is a NOOP. + * + * @return array An array of any generated post-insert IDs. This will be an empty array + * if the entity class does not use the IDENTITY generation strategy. + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return array(); + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->isPostInsertGenerator(); + + $stmt = $this->conn->prepare($this->getInsertSQL()); + $tableName = $this->class->getTableName(); + + foreach ($this->queuedInserts as $entity) { + $insertData = $this->prepareInsertData($entity); + + if (isset($insertData[$tableName])) { + $paramIndex = 1; + + foreach ($insertData[$tableName] as $column => $value) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]); + } + } + + $stmt->execute(); + + if ($isPostInsertId) { + $id = $idGenerator->generate($this->em, $entity); + $postInsertIds[$id] = $entity; + } else { + $id = $this->class->getIdentifierValues($entity); + } + + if ($this->class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + } + + $stmt->closeCursor(); + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * Retrieves the default version value which was created + * by the preceding INSERT statement and assigns it back in to the + * entities version field. + * + * @param object $entity + * @param mixed $id + * + * @return void + */ + protected function assignDefaultVersionValue($entity, $id) + { + $value = $this->fetchVersionValue($this->class, $id); + + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } + + /** + * Fetches the current version value of a versioned entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass + * @param mixed $id + * + * @return mixed + */ + protected function fetchVersionValue($versionedClass, $id) + { + $versionField = $versionedClass->versionField; + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); + $columnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform); + + //FIXME: Order with composite keys might not be correct + $sql = 'SELECT ' . $columnName + . ' FROM ' . $tableName + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; + + $value = $this->conn->fetchColumn($sql, array_values((array) $id)); + + return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->platform); + } + + /** + * Updates a managed entity. The entity is updated according to its current changeset + * in the running UnitOfWork. If there is no changeset, nothing is updated. + * + * The data to update is retrieved through {@link prepareUpdateData}. + * Subclasses that override this method are supposed to obtain the update data + * in the same way, through {@link prepareUpdateData}. + * + * Subclasses are also supposed to take care of versioning when overriding this method, + * if necessary. The {@link updateTable} method can be used to apply the data retrieved + * from {@prepareUpdateData} on the target tables, thereby optionally applying versioning. + * + * @param object $entity The entity to update. + * + * @return void + */ + public function update($entity) + { + $tableName = $this->class->getTableName(); + $updateData = $this->prepareUpdateData($entity); + + if ( ! isset($updateData[$tableName]) || ! ($data = $updateData[$tableName])) { + return; + } + + $isVersioned = $this->class->isVersioned; + $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $this->updateTable($entity, $quotedTableName, $data, $isVersioned); + + if ($isVersioned) { + $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + + $this->assignDefaultVersionValue($entity, $id); + } + } + + /** + * Performs an UPDATE statement for an entity on a specific table. + * The UPDATE can optionally be versioned, which requires the entity to have a version field. + * + * @param object $entity The entity object being updated. + * @param string $quotedTableName The quoted name of the table to apply the UPDATE on. + * @param array $updateData The map of columns to update (column => value). + * @param boolean $versioned Whether the UPDATE should be versioned. + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ + protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false) + { + $set = array(); + $types = array(); + $params = array(); + + foreach ($updateData as $columnName => $value) { + $placeholder = '?'; + $column = $columnName; + + switch (true) { + case isset($this->class->fieldNames[$columnName]): + $fieldName = $this->class->fieldNames[$columnName]; + $column = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + + if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($this->columnTypes[$columnName]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + break; + + case isset($this->quotedColumns[$columnName]): + $column = $this->quotedColumns[$columnName]; + + break; + } + + $params[] = $value; + $set[] = $column . ' = ' . $placeholder; + $types[] = $this->columnTypes[$columnName]; + } + + $where = array(); + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + + foreach ($this->class->identifier as $idField) { + if ( ! isset($this->class->associationMappings[$idField])) { + + $params[] = $identifier[$idField]; + $types[] = $this->class->fieldMappings[$idField]['type']; + $where[] = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform); + + continue; + } + + $params[] = $identifier[$idField]; + $where[] = $this->class->associationMappings[$idField]['joinColumns'][0]['name']; + $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']); + + switch (true) { + case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + break; + + case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + break; + + default: + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + } + + } + + if ($versioned) { + $versionField = $this->class->versionField; + $versionFieldType = $this->class->fieldMappings[$versionField]['type']; + $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform); + + $where[] = $versionColumn; + $types[] = $this->class->fieldMappings[$versionField]['type']; + $params[] = $this->class->reflFields[$versionField]->getValue($entity); + + switch ($versionFieldType) { + case Type::INTEGER: + $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; + break; + + case Type::DATETIME: + $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; + break; + } + } + + $sql = 'UPDATE ' . $quotedTableName + . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; + + $result = $this->conn->executeUpdate($sql, $params, $types); + + if ($versioned && ! $result) { + throw OptimisticLockException::lockFailed($entity); + } + } + + /** + * @todo Add check for platform if it supports foreign keys/cascading. + * + * @param array $identifier + * + * @return void + */ + protected function deleteJoinTableRecords($identifier) + { + foreach ($this->class->associationMappings as $mapping) { + if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY) { + continue; + } + + // @Todo this only covers scenarios with no inheritance or of the same level. Is there something + // like self-referential relationship between different levels of an inheritance hierarchy? I hope not! + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); + $class = $this->class; + $association = $mapping; + $otherColumns = array(); + $otherKeys = array(); + $keys = array(); + + if ( ! $mapping['isOwningSide']) { + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $association = $class->associationMappings[$mapping['mappedBy']]; + } + + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + + if ($selfReferential) { + $otherColumns = (! $mapping['isOwningSide']) + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + } + + foreach ($joinColumns as $joinColumn) { + $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($otherColumns as $joinColumn) { + $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + if (isset($mapping['isOnDeleteCascade'])) { + continue; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + + $this->conn->delete($joinTableName, array_combine($keys, $identifier)); + + if ($selfReferential) { + $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier)); + } + } + } + + /** + * Deletes a managed entity. + * + * The entity to delete must be managed and have a persistent identifier. + * The deletion happens instantaneously. + * + * Subclasses may override this method to customize the semantics of entity deletion. + * + * @param object $entity The entity to delete. + * + * @return void + */ + public function delete($entity) + { + $class = $this->class; + $em = $this->em; + + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $tableName = $this->quoteStrategy->getTableName($class, $this->platform); + $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); + $id = array_combine($idColumns, $identifier); + + $types = array_map(function ($identifier) use ($class, $em) { + if (isset($class->fieldMappings[$identifier])) { + return $class->fieldMappings[$identifier]['type']; + } + + $targetMapping = $em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']); + + if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) { + return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + } + + if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { + $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + } + + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + + }, $class->identifier); + + $this->deleteJoinTableRecords($identifier); + $this->conn->delete($tableName, $id, $types); + } + + /** + * Prepares the changeset of an entity for database insertion (UPDATE). + * + * The changeset is obtained from the currently running UnitOfWork. + * + * During this preparation the array that is passed as the second parameter is filled with + * => pairs, grouped by table name. + * + * Example: + * + * array( + * 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...), + * 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...), + * ... + * ) + * + * + * @param object $entity The entity for which to prepare the data. + * + * @return array The prepared data. + */ + protected function prepareUpdateData($entity) + { + $result = array(); + $uow = $this->em->getUnitOfWork(); + + if (($versioned = $this->class->isVersioned) != false) { + $versionField = $this->class->versionField; + } + + foreach ($uow->getEntityChangeSet($entity) as $field => $change) { + if ($versioned && $versionField == $field) { + continue; + } + + $newVal = $change[1]; + + if ( ! isset($this->class->associationMappings[$field])) { + + $columnName = $this->class->columnNames[$field]; + $this->columnTypes[$columnName] = $this->class->fieldMappings[$field]['type']; + $result[$this->getOwningTable($field)][$columnName] = $newVal; + + continue; + } + + $assoc = $this->class->associationMappings[$field]; + + // Only owning side of x-1 associations can have a FK column. + if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + if ($newVal !== null) { + $oid = spl_object_hash($newVal); + + if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { + // The associated entity $newVal is not yet persisted, so we must + // set $newVal = null, in order to insert a null value and schedule an + // extra update on the UnitOfWork. + $uow->scheduleExtraUpdate($entity, array($field => array(null, $newVal))); + + $newVal = null; + } + } + + if ($newVal !== null) { + $newValId = $uow->getEntityIdentifier($newVal); + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $owningTable = $this->getOwningTable($field); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $sourceColumn = $joinColumn['name']; + $targetColumn = $joinColumn['referencedColumnName']; + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + + $this->quotedColumns[$sourceColumn] = $quotedColumn; + $this->columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); + + switch (true) { + case $newVal === null: + $value = null; + break; + + case $targetClass->containsForeignIdentifier: + $value = $newValId[$targetClass->getFieldForColumn($targetColumn)]; + break; + + default: + $value = $newValId[$targetClass->fieldNames[$targetColumn]]; + break; + } + + $result[$owningTable][$sourceColumn] = $value; + } + } + + return $result; + } + + /** + * Prepares the data changeset of a managed entity for database insertion (initial INSERT). + * The changeset of the entity is obtained from the currently running UnitOfWork. + * + * The default insert data preparation is the same as for updates. + * + * @param object $entity The entity for which to prepare the data. + * + * @return array The prepared data for the tables to update. + * + * @see prepareUpdateData + */ + protected function prepareInsertData($entity) + { + return $this->prepareUpdateData($entity); + } + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * The default implementation in BasicEntityPersister always returns the name + * of the table the entity type of this persister is mapped to, since an entity + * is always persisted to a single table with a BasicEntityPersister. + * + * @param string $fieldName The field name. + * + * @return string The table name. + */ + public function getOwningTable($fieldName) + { + return $this->class->getTableName(); + } + + /** + * Loads an entity by a list of field criteria. + * + * @param array $criteria The criteria by which to load the entity. + * @param object|null $entity The entity to load the data into. If not specified, a new entity is created. + * @param array|null $assoc The association that connects the entity to load to another entity, if any. + * @param array $hints Hints for entity creation. + * @param int $lockMode + * @param int|null $limit Limit number of results. + * @param array|null $orderBy Criteria to order by. + * + * @return object|null The loaded and managed entity instance or NULL if the entity can not be found. + * + * @todo Check identity map? loadById method? Try to guess whether $criteria is the id? + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null, array $orderBy = null) + { + $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + if ($entity !== null) { + $hints[Query::HINT_REFRESH] = true; + $hints[Query::HINT_REFRESH_ENTITY] = $entity; + } + + $hydrator = $this->em->newHydrator($this->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + $entities = $hydrator->hydrateAll($stmt, $this->rsm, $hints); + + return $entities ? $entities[0] : null; + } + + /** + * Loads an entity of this persister's mapped class as part of a single-valued + * association from another entity. + * + * @param array $assoc The association to load. + * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). + * @param array $identifier The identifier of the entity to load. Must be provided if + * the association to load represents the owning side, otherwise + * the identifier is derived from the $sourceEntity. + * + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) + { + if (($foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) { + return $foundEntity; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ($assoc['isOwningSide']) { + $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); + + // Mark inverse side as fetched in the hints, otherwise the UoW would + // try to load it in a separate query (remember: to-one inverse sides can not be lazy). + $hints = array(); + + if ($isInverseSingleValued) { + $hints['fetched']["r"][$assoc['inversedBy']] = true; + } + + /* cascade read-only status + if ($this->em->getUnitOfWork()->isReadOnly($sourceEntity)) { + $hints[Query::HINT_READ_ONLY] = true; + } + */ + + $targetEntity = $this->load($identifier, null, $assoc, $hints); + + // Complete bidirectional association, if necessary + if ($targetEntity !== null && $isInverseSingleValued) { + $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); + } + + return $targetEntity; + } + + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); + + // TRICKY: since the association is specular source and target are flipped + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + // unset the old value and set the new sql aliased value here. By definition + // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. + $identifier[$this->getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = + $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + + unset($identifier[$targetKeyColumn]); + } + + $targetEntity = $this->load($identifier, null, $assoc); + + if ($targetEntity !== null) { + $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); + } + + return $targetEntity; + } + + /** + * Refreshes a managed entity. + * + * @param array $id The identifier of the entity as an associative array from + * column or field names to values. + * @param object $entity The entity to refresh. + * @param int $lockMode + * + * @return void + */ + public function refresh(array $id, $entity, $lockMode = 0) + { + $sql = $this->getSelectSQL($id, null, $lockMode); + list($params, $types) = $this->expandParameters($id); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); + $hydrator->hydrateAll($stmt, $this->rsm, array(Query::HINT_REFRESH => true)); + } + + /** + * Loads Entities matching the given Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(Criteria $criteria) + { + $orderBy = $criteria->getOrderings(); + $limit = $criteria->getMaxResults(); + $offset = $criteria->getFirstResult(); + $query = $this->getSelectSQL($criteria, null, 0, $limit, $offset, $orderBy); + + list($params, $types) = $this->expandCriteriaParameters($criteria); + + $stmt = $this->conn->executeQuery($query, $params, $types); + $hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * Expands Criteria Parameters by walking the expressions and grabbing all + * parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array(array(), array()) + */ + private function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return array(array(), array()); + } + + $valueVisitor = new SqlValueVisitor(); + + $valueVisitor->dispatch($expression); + + list($values, $types) = $valueVisitor->getParamsAndTypes(); + + $sqlValues = array(); + foreach ($values as $value) { + $sqlValues[] = $this->getValue($value); + } + + $sqlTypes = array(); + foreach ($types as $type) { + list($field, $value) = $type; + $sqlTypes[] = $this->getType($field, $value); + } + + return array($sqlValues, $sqlTypes); + } + + /** + * Loads a list of entities by a list of field criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $sql = $this->getSelectSQL($criteria, null, 0, $limit, $offset, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * Gets (sliced or full) elements of the given collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return array + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads an array of entities from a given DBAL statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * + * @return array + */ + private function loadArrayFromStatement($assoc, $stmt) + { + $rsm = $this->rsm; + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Hydrates a collection from a given DBAL statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * @param PersistentCollection $coll + * + * @return array + */ + private function loadCollectionFromStatement($assoc, $stmt, $coll) + { + $rsm = $this->rsm; + $hints = array( + UnitOfWork::HINT_DEFEREAGERLOAD => true, + 'collection' => $coll + ); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Loads a collection of entities of a many-to-many association. + * + * @param array $assoc The association mapping of the association being loaded. + * @param object $sourceEntity The entity that owns the collection. + * @param PersistentCollection $coll The collection to fill. + * + * @return array + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $class = $sourceClass; + $association = $assoc; + $criteria = array(); + + + if ( ! $assoc['isOwningSide']) { + $class = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $class->associationMappings[$assoc['mappedBy']]; + } + + $joinColumns = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + + foreach ($joinColumns as $joinColumn) { + + $sourceKeyColumn = $joinColumn['referencedColumnName']; + $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + + switch (true) { + case $sourceClass->containsForeignIdentifier: + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + break; + + case isset($sourceClass->fieldNames[$sourceKeyColumn]): + $field = $sourceClass->fieldNames[$sourceKeyColumn]; + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + break; + + default: + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value; + } + + $sql = $this->getSelectSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the SELECT SQL to select one or more entities by a set of field criteria. + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @param array|null $assoc + * @param int $lockMode + * @param int|null $limit + * @param int|null $offset + * @param array|null $orderBy + * + * @return string + */ + protected function getSelectSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $lockSql = ''; + $joinSql = ''; + $orderBySql = ''; + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql = $this->getSelectManyToManyJoinSQL($assoc); + } + + if (isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name)); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria, $assoc); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = ' ' . $this->platform->getReadLockSql(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $lockSql = ' ' . $this->platform->getWriteLockSql(); + break; + } + + $columnList = $this->getSelectColumnsSQL(); + $tableAlias = $this->getSQLTableAlias($this->class->name); + $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $select = 'SELECT ' . $columnList; + $from = ' FROM ' . $tableName . ' '. $tableAlias; + $join = $this->selectJoinSql . $joinSql; + $where = ($conditionSql ? ' WHERE ' . $conditionSql : ''); + $lock = $this->platform->appendLockHint($from, $lockMode); + $query = $select + . $lock + . $join + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * Gets the ORDER BY SQL snippet for ordered collections. + * + * @param array $orderBy + * @param string $baseTableAlias + * + * @return string + * + * @throws \Doctrine\ORM\ORMException + */ + protected final function getOrderBySQL(array $orderBy, $baseTableAlias) + { + $orderByList = array(); + + foreach ($orderBy as $fieldName => $orientation) { + + $orientation = strtoupper(trim($orientation)); + + if ($orientation != 'ASC' && $orientation != 'DESC') { + throw ORMException::invalidOrientation($this->class->name, $fieldName); + } + + if (isset($this->class->fieldMappings[$fieldName])) { + $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited']) + : $baseTableAlias; + + $columnName = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + + continue; + } + + if (isset($this->class->associationMappings[$fieldName])) { + + if ( ! $this->class->associationMappings[$fieldName]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $fieldName); + } + + $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited']) + : $baseTableAlias; + + foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + } + + continue; + } + + throw ORMException::unrecognizedField($fieldName); + } + + return ' ORDER BY ' . implode(', ', $orderByList); + } + + /** + * Gets the SQL fragment with the list of columns to select when querying for + * an entity in this persister. + * + * Subclasses should override this method to alter or change the select column + * list SQL fragment. Note that in the implementation of BasicEntityPersister + * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}. + * Subclasses may or may not do the same. + * + * @return string The SQL fragment. + */ + protected function getSelectColumnsSQL() + { + if ($this->selectColumnListSql !== null) { + return $this->selectColumnListSql; + } + + $columnList = array(); + $this->rsm = new Query\ResultSetMapping(); + $this->rsm->addEntityResult($this->class->name, 'r'); // r for root + + // Add regular columns to select list + foreach ($this->class->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $this->class); + } + + $this->selectJoinSql = ''; + $eagerAliasCounter = 0; + + foreach ($this->class->associationMappings as $assocField => $assoc) { + $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class); + + if ($assocColumnSQL) { + $columnList[] = $assocColumnSQL; + } + + if ( ! (($assoc['type'] & ClassMetadata::TO_ONE) && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide']))) { + continue; + } + + $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + + if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { + continue; // now this is why you shouldn't use inheritance + } + + $assocAlias = 'e' . ($eagerAliasCounter++); + $this->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + + foreach ($eagerEntity->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias); + } + + foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) { + $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL( + $eagerAssocField, $eagerAssoc, $eagerEntity, $assocAlias + ); + + if ($eagerAssocColumnSQL) { + $columnList[] = $eagerAssocColumnSQL; + } + } + + $association = $assoc; + $joinCondition = array(); + + if ( ! $assoc['isOwningSide']) { + $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + } + + $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias); + $joinTableName = $this->quoteStrategy->getTableName($eagerEntity, $this->platform); + + if ($assoc['isOwningSide']) { + $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias); + $this->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']); + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity']) + . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol; + } + + // Add filter SQL + if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { + $joinCondition[] = $filterSql; + } + + } else { + + $this->selectJoinSql .= ' LEFT JOIN'; + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol; + } + } + + $this->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON '; + $this->selectJoinSql .= implode(' AND ', $joinCondition); + } + + $this->selectColumnListSql = implode(', ', $columnList); + + return $this->selectColumnListSql; + } + + /** + * Gets the SQL join fragment used when selecting entities from an association. + * + * @param string $field + * @param array $assoc + * @param ClassMetadata $class + * @param string $alias + * + * @return string + */ + protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') + { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) ) { + return ''; + } + + $columnList = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); + $columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) + . '.' . $quotedColumn . ' AS ' . $resultColumnName; + + $this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true); + } + + return implode(', ', $columnList); + } + + /** + * Gets the SQL join fragment used when selecting entities from a + * many-to-many association. + * + * @param array $manyToMany + * + * @return string + */ + protected function getSelectManyToManyJoinSQL(array $manyToMany) + { + $conditions = array(); + $association = $manyToMany; + $sourceTableAlias = $this->getSQLTableAlias($this->class->name); + + if ( ! $manyToMany['isOwningSide']) { + $targetEntity = $this->em->getClassMetadata($manyToMany['targetEntity']); + $association = $targetEntity->associationMappings[$manyToMany['mappedBy']]; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + $joinColumns = ($manyToMany['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn; + } + + return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions); + } + + /** + * Gets the INSERT SQL used by the persister to persist a new entity. + * + * @return string + */ + protected function getInsertSQL() + { + if ($this->insertSql !== null) { + return $this->insertSql; + } + + $columns = $this->getInsertColumnList(); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if (empty($columns)) { + $identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform); + $this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn); + + return $this->insertSql; + } + + $values = array(); + $columns = array_unique($columns); + + foreach ($columns as $column) { + $placeholder = '?'; + + if (isset($this->class->fieldNames[$column]) + && isset($this->columnTypes[$this->class->fieldNames[$column]]) + && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) { + + $type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + $values[] = $placeholder; + } + + $columns = implode(', ', $columns); + $values = implode(', ', $values); + + $this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values); + + return $this->insertSql; + } + + /** + * Gets the list of columns to put in the INSERT SQL statement. + * + * Subclasses should override this method to alter or change the list of + * columns placed in the INSERT statements used by the persister. + * + * @return array The list of columns. + */ + protected function getInsertColumnList() + { + $columns = array(); + + foreach ($this->class->reflFields as $name => $field) { + if ($this->class->isVersioned && $this->class->versionField == $name) { + continue; + } + + if (isset($this->class->associationMappings[$name])) { + $assoc = $this->class->associationMappings[$name]; + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + } + + continue; + } + + if ($this->class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->class->identifier[0] != $name) { + $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); + $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + } + } + + return $columns; + } + + /** + * Gets the SQL snippet of a qualified column name for the given field name. + * + * @param string $field The field name. + * @param ClassMetadata $class The class that declares this field. The table this class is + * mapped to must own the column for the given field. + * @param string $alias + * + * @return string + */ + protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $root = $alias == 'r' ? '' : $alias ; + $tableAlias = $this->getSQLTableAlias($class->name, $root); + $columnName = $this->quoteStrategy->getColumnName($field, $class, $this->platform); + $sql = $tableAlias . '.' . $columnName; + $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]); + + $this->rsm->addFieldResult($alias, $columnAlias, $field); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + /** + * Gets the SQL table alias for the given class name. + * + * @param string $className + * @param string $assocName + * + * @return string The SQL table alias. + * + * @todo Reconsider. Binding table aliases to class names is not such a good idea. + */ + protected function getSQLTableAlias($className, $assocName = '') + { + if ($assocName) { + $className .= '#' . $assocName; + } + + if (isset($this->sqlTableAliases[$className])) { + return $this->sqlTableAliases[$className]; + } + + $tableAlias = 't' . $this->sqlAliasCounter++; + + $this->sqlTableAliases[$className] = $tableAlias; + + return $tableAlias; + } + + /** + * Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode. + * + * @param array $criteria + * @param int $lockMode + * + * @return void + */ + public function lock(array $criteria, $lockMode) + { + $lockSql = ''; + $conditionSql = $this->getSelectConditionSQL($criteria); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = $this->platform->getReadLockSql(); + + break; + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = $this->platform->getWriteLockSql(); + break; + } + + $lock = $this->platform->appendLockHint($this->getLockTablesSql(), $lockMode); + $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' '; + $sql = 'SELECT 1 ' + . $lock + . $where + . $lockSql; + + list($params, $types) = $this->expandParameters($criteria); + + $this->conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + protected function getLockTablesSql() + { + return 'FROM ' + . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' ' + . $this->getSQLTableAlias($this->class->name); + } + + /** + * Gets the Select Where Condition from a Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return string + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return ''; + } + + $visitor = new SqlExpressionVisitor($this, $this->class); + + return $visitor->dispatch($expression); + } + + /** + * Gets the SQL WHERE condition for matching a field with a given value. + * + * @param string $field + * @param mixed $value + * @param array|null $assoc + * @param string|null $comparison + * + * @return string + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + $placeholder = '?'; + $condition = $this->getSelectConditionStatementColumnSQL($field, $assoc); + + if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) { + $placeholder = Type::getType($this->class->getTypeOfField($field))->convertToDatabaseValueSQL($placeholder, $this->platform); + } + + if ($comparison !== null) { + + // special case null value handling + if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && $value === null) { + return $condition . ' IS NULL'; + } else if ($comparison === Comparison::NEQ && $value === null) { + return $condition . ' IS NOT NULL'; + } + + return $condition . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); + } + + if (is_array($value)) { + return sprintf('%s IN (%s)' , $condition, $placeholder); + } + + if ($value === null) { + return sprintf('%s IS NULL' , $condition); + } + + return sprintf('%s = %s' , $condition, $placeholder); + } + + /** + * Builds the left-hand-side of a where condition statement. + * + * @param string $field + * @param array|null $assoc + * + * @return string + * + * @throws \Doctrine\ORM\ORMException + */ + protected function getSelectConditionStatementColumnSQL($field, $assoc = null) + { + if (isset($this->class->columnNames[$field])) { + $className = (isset($this->class->fieldMappings[$field]['inherited'])) + ? $this->class->fieldMappings[$field]['inherited'] + : $this->class->name; + + return $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform); + } + + if (isset($this->class->associationMappings[$field])) { + + if ( ! $this->class->associationMappings[$field]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $field); + } + + $joinColumn = $this->class->associationMappings[$field]['joinColumns'][0]; + $className = (isset($this->class->associationMappings[$field]['inherited'])) + ? $this->class->associationMappings[$field]['inherited'] + : $this->class->name; + + return $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + + if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { + // very careless developers could potentially open up this normally hidden api for userland attacks, + // therefore checking for spaces and function calls which are not allowed. + + // found a join column condition, not really a "field" + return $field; + } + + throw ORMException::unrecognizedField($field); + } + + /** + * Gets the conditional SQL fragment used in the WHERE clause when selecting + * entities in this persister. + * + * Subclasses are supposed to override this method if they intend to change + * or alter the criteria by which entities are selected. + * + * @param array $criteria + * @param array|null $assoc + * + * @return string + */ + protected function getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditions = array(); + + foreach ($criteria as $field => $value) { + $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc); + } + + return implode(' AND ', $conditions); + } + + /** + * Returns an array with (sliced or full list) of elements in the specified collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return array + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads a collection of entities in a one-to-many association. + * + * @param array $assoc + * @param object $sourceEntity + * @param PersistentCollection $coll The collection to load/fill. + * + * @return array + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * Builds criteria and execute SQL statement to fetch the one to many entities from. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return \Doctrine\DBAL\Statement + */ + private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $criteria = array(); + $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']]; + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + + $tableAlias = $this->getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->class->name); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ($sourceClass->containsForeignIdentifier) { + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; + + continue; + } + + $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } + + $sql = $this->getSelectSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * Expands the parameters from the given criteria and use the correct binding types if found. + * + * @param array $criteria + * + * @return array + */ + private function expandParameters($criteria) + { + $params = array(); + $types = array(); + + foreach ($criteria as $field => $value) { + if ($value === null) { + continue; // skip null values. + } + + $types[] = $this->getType($field, $value); + $params[] = $this->getValue($value); + } + + return array($params, $types); + } + + /** + * Infers field type to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * + * @return integer + * + * @throws \Doctrine\ORM\Query\QueryException + */ + private function getType($field, $value) + { + switch (true) { + case (isset($this->class->fieldMappings[$field])): + $type = $this->class->fieldMappings[$field]['type']; + break; + + case (isset($this->class->associationMappings[$field])): + $assoc = $this->class->associationMappings[$field]; + + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw Query\QueryException::associationPathCompositeKeyNotSupported(); + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetColumn = $assoc['joinColumns'][0]['referencedColumnName']; + $type = null; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + break; + + default: + $type = null; + } + + if (is_array($value)) { + $type = Type::getType($type)->getBindingType(); + $type += Connection::ARRAY_PARAM_OFFSET; + } + + return $type; + } + + /** + * Retrieves parameter value. + * + * @param mixed $value + * + * @return mixed + */ + private function getValue($value) + { + if ( ! is_array($value)) { + return $this->getIndividualValue($value); + } + + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue[] = $this->getIndividualValue($itemValue); + } + + return $newValue; + } + + /** + * Retrieves an individual parameter value. + * + * @param mixed $value + * + * @return mixed + */ + private function getIndividualValue($value) + { + if ( ! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + return $value; + } + + return $this->em->getUnitOfWork()->getSingleIdentifierValue($value); + } + + /** + * Checks whether the given managed entity exists in the database. + * + * @param object $entity + * @param array $extraConditions + * + * @return boolean TRUE if the entity exists in the database, FALSE otherwise. + */ + public function exists($entity, array $extraConditions = array()) + { + $criteria = $this->class->getIdentifierValues($entity); + + if ( ! $criteria) { + return false; + } + + if ($extraConditions) { + $criteria = array_merge($criteria, $extraConditions); + } + + $alias = $this->getSQLTableAlias($this->class->name); + + $sql = 'SELECT 1 ' + . $this->getLockTablesSql() + . ' WHERE ' . $this->getSelectConditionSQL($criteria); + + if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) { + $sql .= ' AND ' . $filterSql; + } + + list($params) = $this->expandParameters($criteria); + + return (bool) $this->conn->fetchColumn($sql, $params); + } + + /** + * Generates the appropriate join SQL for the given join column. + * + * @param array $joinColumns The join columns definition of an association. + * + * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. + */ + protected function getJoinSQLForJoinColumns($joinColumns) + { + // if one of the join columns is nullable, return left join + foreach ($joinColumns as $joinColumn) { + if ( ! isset($joinColumn['nullable']) || $joinColumn['nullable']) { + return 'LEFT JOIN'; + } + } + + return 'INNER JOIN'; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * + * @return string + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->sqlAliasCounter++, $this->platform); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php new file mode 100644 index 00000000..04da1f7d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php @@ -0,0 +1,29 @@ +. + */ +namespace Doctrine\ORM\Persisters; + +/** + * Persister for collections of basic elements / value types. + * + * @author robo + * @todo Implementation once support for collections of basic elements (i.e. strings) is added. + */ +abstract class ElementCollectionPersister extends AbstractCollectionPersister +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php new file mode 100644 index 00000000..9300c36c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -0,0 +1,560 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; + +use Doctrine\Common\Collections\Criteria; + +/** + * The joined subclass persister maps a single entity instance to several tables in the + * database as it is defined by the Class Table Inheritance strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @see http://martinfowler.com/eaaCatalog/classTableInheritance.html + */ +class JoinedSubclassPersister extends AbstractEntityInheritancePersister +{ + /** + * Map that maps column names to the table names that own them. + * This is mainly a temporary cache, used during a single request. + * + * @var array + */ + private $owningTableMap = array(); + + /** + * Map of table to quoted table names. + * + * @var array + */ + private $quotedTableMap = array(); + + /** + * {@inheritdoc} + */ + protected function getDiscriminatorColumnTableName() + { + $class = ($this->class->name !== $this->class->rootEntityName) + ? $this->em->getClassMetadata($this->class->rootEntityName) + : $this->class; + + return $class->getTableName(); + } + + /** + * This function finds the ClassMetadata instance in an inheritance hierarchy + * that is responsible for enabling versioning. + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getVersionedClassMetadata() + { + if (isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) { + $definingClassName = $this->class->fieldMappings[$this->class->versionField]['inherited']; + + return $this->em->getClassMetadata($definingClassName); + } + + return $this->class; + } + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * @param string $fieldName + * + * @return string + * + * @override + */ + public function getOwningTable($fieldName) + { + if (isset($this->owningTableMap[$fieldName])) { + return $this->owningTableMap[$fieldName]; + } + + switch (true) { + case isset($this->class->associationMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->associationMappings[$fieldName]['inherited']); + break; + + case isset($this->class->fieldMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]['inherited']); + break; + + default: + $cm = $this->class; + break; + } + + $tableName = $cm->getTableName(); + $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform); + + $this->owningTableMap[$fieldName] = $tableName; + $this->quotedTableMap[$tableName] = $quotedTableName; + + return $tableName; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return; + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->isPostInsertGenerator(); + $rootClass = ($this->class->name !== $this->class->rootEntityName) + ? $this->em->getClassMetadata($this->class->rootEntityName) + : $this->class; + + // Prepare statement for the root table + $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->name); + $rootTableName = $rootClass->getTableName(); + $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL()); + + // Prepare statements for sub tables. + $subTableStmts = array(); + + if ($rootClass !== $this->class) { + $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL()); + } + + foreach ($this->class->parentClasses as $parentClassName) { + $parentClass = $this->em->getClassMetadata($parentClassName); + $parentTableName = $parentClass->getTableName(); + + if ($parentClass !== $rootClass) { + $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClassName); + $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL()); + } + } + + // Execute all inserts. For each entity: + // 1) Insert on root table + // 2) Insert on sub tables + foreach ($this->queuedInserts as $entity) { + $insertData = $this->prepareInsertData($entity); + + // Execute insert on root table + $paramIndex = 1; + + foreach ($insertData[$rootTableName] as $columnName => $value) { + $rootTableStmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } + + $rootTableStmt->execute(); + + if ($isPostInsertId) { + $id = $idGenerator->generate($this->em, $entity); + $postInsertIds[$id] = $entity; + } else { + $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + } + + // Execute inserts on subtables. + // The order doesn't matter because all child tables link to the root table via FK. + foreach ($subTableStmts as $tableName => $stmt) { + /** @var \Doctrine\DBAL\Statement $stmt */ + $paramIndex = 1; + $data = isset($insertData[$tableName]) + ? $insertData[$tableName] + : array(); + + foreach ((array) $id as $idName => $idVal) { + $type = isset($this->columnTypes[$idName]) ? $this->columnTypes[$idName] : Type::STRING; + + $stmt->bindValue($paramIndex++, $idVal, $type); + } + + foreach ($data as $columnName => $value) { + if (!isset($id[$columnName])) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } + } + + $stmt->execute(); + } + } + + $rootTableStmt->closeCursor(); + + foreach ($subTableStmts as $stmt) { + $stmt->closeCursor(); + } + + if ($this->class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $updateData = $this->prepareUpdateData($entity); + + if ( ! $updateData) { + return; + } + + if (($isVersioned = $this->class->isVersioned) === false) { + return; + } + + $versionedClass = $this->getVersionedClassMetadata(); + $versionedTable = $versionedClass->getTableName(); + + foreach ($updateData as $tableName => $data) { + $tableName = $this->quotedTableMap[$tableName]; + $versioned = $isVersioned && $versionedTable === $tableName; + + $this->updateTable($entity, $tableName, $data, $versioned); + } + + // Make sure the table with the version column is updated even if no columns on that + // table were affected. + if ($isVersioned) { + if ( ! isset($updateData[$versionedTable])) { + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $this->updateTable($entity, $tableName, array(), true); + } + + $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $identifiers); + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $id = array_combine($this->class->getIdentifierColumnNames(), $identifier); + + $this->deleteJoinTableRecords($identifier); + + // If the database platform supports FKs, just + // delete the row from the root table. Cascades do the rest. + if ($this->platform->supportsForeignKeyConstraints()) { + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); + + $this->conn->delete($rootTable, $id); + + return; + } + + // Delete from all tables individually, starting from this class' table up to the root table. + $rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $this->conn->delete($rootTable, $id); + + foreach ($this->class->parentClasses as $parentClass) { + $parentMetadata = $this->em->getClassMetadata($parentClass); + $parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform); + + $this->conn->delete($parentTable, $id); + } + } + + /** + * {@inheritdoc} + */ + protected function getSelectSQL($criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $joinSql = ''; + $identifierColumn = $this->class->getIdentifierColumnNames(); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $parentClass = $this->em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + // OUTER JOIN sub tables + foreach ($this->class->subClasses as $subClassName) { + $conditions = array(); + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql .= $this->getSelectManyToManyJoinSQL($assoc); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria, $assoc); + + // If the current class in the root entity, add the filters + if ($filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName))) { + $conditionSql .= $conditionSql + ? ' AND ' . $filterSql + : $filterSql; + } + + $orderBySql = ''; + + if ($assoc !== null && isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias); + } + + $lockSql = ''; + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + + $lockSql = ' ' . $this->platform->getReadLockSql(); + + break; + + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = ' ' . $this->platform->getWriteLockSql(); + + break; + } + + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $where = $conditionSql != '' ? ' WHERE ' . $conditionSql : ''; + $columnList = $this->getSelectColumnsSQL(); + $query = 'SELECT ' . $columnList + . ' FROM ' + . $tableName . ' ' . $baseTableAlias + . $joinSql + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * Get the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + public function getLockTablesSql() + { + $joinSql = ''; + $identifierColumns = $this->class->getIdentifierColumnNames(); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $parentClass = $this->em->getClassMetadata($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumns as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + return 'FROM ' . $quotedTableName . ' ' . $baseTableAlias . $joinSql; + } + + /** + * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. + * + * @return string + */ + protected function getSelectColumnsSQL() + { + // Create the column list fragment only once + if ($this->selectColumnListSql !== null) { + return $this->selectColumnListSql; + } + + $columnList = array(); + $this->rsm = new ResultSetMapping(); + $discrColumn = $this->class->discriminatorColumn['name']; + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); + + $this->rsm->addEntityResult($this->class->name, 'r'); + $this->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Add regular columns + foreach ($this->class->fieldMappings as $fieldName => $mapping) { + $class = isset($mapping['inherited']) + ? $this->em->getClassMetadata($mapping['inherited']) + : $this->class; + + $columnList[] = $this->getSelectColumnSQL($fieldName, $class); + } + + // Add foreign key columns + foreach ($this->class->associationMappings as $mapping) { + if ( ! $mapping['isOwningSide'] || ! ($mapping['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $tableAlias = isset($mapping['inherited']) + ? $this->getSQLTableAlias($mapping['inherited']) + : $baseTableAlias; + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $this->class->name; + + $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className); + } + } + + // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). + $tableAlias = ($this->class->rootEntityName == $this->class->name) + ? $baseTableAlias + : $this->getSQLTableAlias($this->class->rootEntityName); + + $columnList[] = $tableAlias . '.' . $discrColumn; + + // sub tables + foreach ($this->class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + + // Add subclass columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); + } + + // Add join columns (foreign keys) + foreach ($subClass->associationMappings as $mapping) { + if ( ! $mapping['isOwningSide'] + || ! ($mapping['type'] & ClassMetadata::TO_ONE) + || isset($mapping['inherited'])) { + continue; + } + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $subClass->name; + + $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className); + } + } + } + + $this->selectColumnListSql = implode(', ', $columnList); + + return $this->selectColumnListSql; + } + + /** + * {@inheritdoc} + */ + protected function getInsertColumnList() + { + // Identifier columns must always come first in the column list of subclasses. + $columns = $this->class->parentClasses + ? $this->class->getIdentifierColumnNames() + : array(); + + foreach ($this->class->reflFields as $name => $field) { + if (isset($this->class->fieldMappings[$name]['inherited']) + && ! isset($this->class->fieldMappings[$name]['id']) + || isset($this->class->associationMappings[$name]['inherited']) + || ($this->class->isVersioned && $this->class->versionField == $name)) { + continue; + } + + if (isset($this->class->associationMappings[$name])) { + $assoc = $this->class->associationMappings[$name]; + if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { + $columns[] = $sourceCol; + } + } + } else if ($this->class->name != $this->class->rootEntityName || + ! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] != $name) { + $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); + $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + } + } + + // Add discriminator column if it is the topmost class. + if ($this->class->name == $this->class->rootEntityName) { + $columns[] = $this->class->discriminatorColumn['name']; + } + + return $columns; + } + + /** + * {@inheritdoc} + */ + protected function assignDefaultVersionValue($entity, $id) + { + $value = $this->fetchVersionValue($this->getVersionedClassMetadata(), $id); + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php new file mode 100644 index 00000000..d9f7e30c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -0,0 +1,461 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; + +/** + * Persister for many-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class ManyToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + * + * @override + */ + protected function getDeleteRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $tableName = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $tableName + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * @override + * + * @internal Order of the parameters must be the same as the order of the columns in getDeleteRowSql. + */ + protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->collectJoinTableColumnParameters($coll, $element); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister + */ + protected function getUpdateRowSQL(PersistentCollection $coll) + { + throw new \BadMethodCallException("Insert Row SQL is not used for ManyToManyPersister"); + } + + /** + * {@inheritdoc} + * + * @override + * + * @internal Order of the parameters must be the same as the order of the columns in getInsertRowSql. + */ + protected function getInsertRowSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')' + . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + } + + /** + * {@inheritdoc} + * + * @override + * + * @internal Order of the parameters must be the same as the order of the columns in getInsertRowSql. + */ + protected function getInsertRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->collectJoinTableColumnParameters($coll, $element); + } + + /** + * Collects the parameters for inserting/deleting on the join table in the order + * of the join table columns as specified in ManyToManyMapping#joinTableColumns. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return array + */ + private function collectJoinTableColumnParameters(PersistentCollection $coll, $element) + { + $params = array(); + $mapping = $coll->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->uow->getEntityIdentifier($coll->getOwner()); + $identifier2 = $this->uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->em->getClassMetadata(get_class($coll->getOwner())); + $class2 = $coll->getTypeClass(); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); + + if ( ! $isComposite) { + $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); + + continue; + } + + if ($isRelationToSource) { + $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; + + continue; + } + + $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; + } + + return $params; + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function getDeleteSQL(PersistentCollection $coll) + { + $columns = array(); + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata(get_class($coll->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $joinTable + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * @override + * + * @internal Order of the parameters must be the same as the order of the columns in getDeleteSql. + */ + protected function getDeleteSQLParameters(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $identifier = $this->uow->getEntityIdentifier($coll->getOwner()); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + return array(reset($identifier)); + } + + // Composite identifier + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + $params[] = isset($sourceClass->fieldNames[$refColumnName]) + ? $identifier[$sourceClass->fieldNames[$refColumnName]] + : $identifier[$sourceClass->getFieldForColumn($columnName)]; + } + + return $params; + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $conditions = array(); + $params = array(); + $mapping = $coll->getMapping(); + $association = $mapping; + $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $id = $this->em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + if ( ! $mapping['isOwningSide']) { + $targetEntity = $this->em->getClassMetadata($mapping['targetEntity']); + $association = $targetEntity->associationMappings[$mapping['mappedBy']]; + } + + $joinColumns = ( ! $mapping['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $referencedName = $joinColumn['referencedColumnName']; + $conditions[] = $columnName . ' = ?'; + $params[] = ($class->containsForeignIdentifier) + ? $id[$class->getFieldForColumn($referencedName)] + : $id[$class->fieldNames[$referencedName]]; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + if ($filterSql) { + $conditions[] = $filterSql; + } + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $joinTableName . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->conn->fetchColumn($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param int $offset + * @param int|null $length + * + * @return array + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + + return $this->em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $uow = $this->em->getUnitOfWork(); + + // Shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->fetchColumn($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // If Entity is scheduled for inclusion, it is not in this collection. + // We can assure that because it would have return true before on array check + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, false); + + $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->executeUpdate($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array + */ + private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters) + { + $uow = $this->em->getUnitOfWork(); + $filterMapping = $coll->getMapping(); + $mapping = $filterMapping; + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $uow->getEntityIdentifier($element); + $targetId = $uow->getEntityIdentifier($coll->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceId = $uow->getEntityIdentifier($coll->getOwner()); + $targetId = $uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); + $whereClauses = array(); + $params = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $params[] = ($targetClass->containsForeignIdentifier) + ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] + : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + continue; + } + + // relationToSourceKeyColumns + $params[] = ($sourceClass->containsForeignIdentifier) + ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] + : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + if ($filterSql) { + $quotedJoinTable .= ' t ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params); + } + + /** + * Generates the filter SQL for a given mapping. + * + * This method is not used for actually grabbing the related entities + * but when the extra-lazy collection methods are called on a filtered + * association. This is why besides the many to many table we also + * have to join in the actual entities table leading to additional + * JOIN. + * + * @param array $mapping Array containing mapping information. + * + * @return string The SQL query part to add to a query. + */ + public function getFilterSql($mapping) + { + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); + $filterSql = $this->generateFilterConditionSQL($rootClass, 'te'); + + if ('' === $filterSql) { + return array('', ''); + } + + $conditions = array(); + $association = $mapping; + + if ( ! $mapping['isOwningSide']) { + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $association = $class->associationMappings[$mapping['mappedBy']]; + } + + // A join is needed if there is filtering on the target entity + $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform); + $joinSql = ' JOIN ' . $tableName . ' te' . ' ON'; + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName; + } + + $joinSql .= implode(' AND ', $conditions); + + return array($joinSql, $filterSql); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php new file mode 100644 index 00000000..911d699f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -0,0 +1,229 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; + +/** + * Persister for one-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class OneToManyPersister extends AbstractCollectionPersister +{ + /** + * Generates the SQL UPDATE that updates a particular row's foreign + * key to null. + * + * @param \Doctrine\ORM\PersistentCollection $coll + * + * @return string + * + * @override + */ + protected function getDeleteRowSQL(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $tableName = $this->quoteStrategy->getTableName($class, $this->platform); + $idColumns = $class->getIdentifierColumnNames(); + + return 'DELETE FROM ' . $tableName + . ' WHERE ' . implode('= ? AND ', $idColumns) . ' = ?'; + } + + /** + * {@inheritdoc} + */ + protected function getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return array_values($this->uow->getEntityIdentifier($element)); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister. + */ + protected function getInsertRowSQL(PersistentCollection $coll) + { + throw new \BadMethodCallException("Insert Row SQL is not used for OneToManyPersister"); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister. + */ + protected function getInsertRowSQLParameters(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Insert Row SQL is not used for OneToManyPersister"); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister. + */ + protected function getUpdateRowSQL(PersistentCollection $coll) + { + throw new \BadMethodCallException("Update Row SQL is not used for OneToManyPersister"); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister. + */ + protected function getDeleteSQL(PersistentCollection $coll) + { + throw new \BadMethodCallException("Update Row SQL is not used for OneToManyPersister"); + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException Not used for OneToManyPersister. + */ + protected function getDeleteSQLParameters(PersistentCollection $coll) + { + throw new \BadMethodCallException("Update Row SQL is not used for OneToManyPersister"); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $id = $this->em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + $whereClauses = array(); + $params = array(); + + $joinColumns = $targetClass->associationMappings[$mapping['mappedBy']]['joinColumns']; + foreach ($joinColumns as $joinColumn) { + $whereClauses[] = $joinColumn['name'] . ' = ?'; + + $params[] = ($targetClass->containsForeignIdentifier) + ? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])] + : $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]]; + } + + $filterTargetClass = $this->em->getClassMetadata($targetClass->rootEntityName); + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) { + $whereClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = 'SELECT count(*)' + . ' FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t' + . ' WHERE ' . implode(' AND ', $whereClauses); + + return $this->conn->fetchColumn($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param int $offset + * @param int|null $length + * + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + $uow = $this->em->getUnitOfWork(); + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $mapping = $coll->getMapping(); + $uow = $this->em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + // only works with single id identifier entities. Will throw an + // exception in Entity Persisters if that is not the case for the + // 'mappedBy' field. + $id = current( $uow->getEntityIdentifier($coll->getOwner())); + + return $persister->exists($element, array($mapping['mappedBy'] => $id)); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // If Entity is scheduled for inclusion, it is not in this collection. + // We can assure that because it would have return true before on array check + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + $mapping = $coll->getMapping(); + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform) + . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; + + return (bool) $this->conn->executeUpdate($sql, $this->getDeleteRowSQLParameters($coll, $element)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php new file mode 100644 index 00000000..111455e5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php @@ -0,0 +1,20 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Collections\Criteria; + +/** + * Persister for entities that participate in a hierarchy mapped with the + * SINGLE_TABLE strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html + */ +class SingleTablePersister extends AbstractEntityInheritancePersister +{ + /** + * {@inheritdoc} + */ + protected function getDiscriminatorColumnTableName() + { + return $this->class->getTableName(); + } + + /** + * {@inheritdoc} + */ + protected function getSelectColumnsSQL() + { + if ($this->selectColumnListSql !== null) { + return $this->selectColumnListSql; + } + + $columnList[] = parent::getSelectColumnsSQL(); + + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $tableAlias = $this->getSQLTableAlias($rootClass->name); + + // Append discriminator column + $discrColumn = $this->class->discriminatorColumn['name']; + $columnList[] = $tableAlias . '.' . $discrColumn; + + $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); + + $this->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Append subclass columns + foreach ($this->class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + + // Regular columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); + } + + // Foreign key columns + foreach ($subClass->associationMappings as $assoc) { + if ( ! $assoc['isOwningSide'] + || ! ($assoc['type'] & ClassMetadata::TO_ONE) + || isset($assoc['inherited'])) { + continue; + } + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($assoc['inherited']) ? $assoc['inherited'] : $this->class->name; + $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className); + } + } + } + + $this->selectColumnListSql = implode(', ', $columnList); + + return $this->selectColumnListSql; + } + + /** + * {@inheritdoc} + */ + protected function getInsertColumnList() + { + $columns = parent::getInsertColumnList(); + + // Add discriminator column to the INSERT SQL + $columns[] = $this->class->discriminatorColumn['name']; + + return $columns; + } + + /** + * {@inheritdoc} + */ + protected function getSQLTableAlias($className, $assocName = '') + { + return parent::getSQLTableAlias($this->class->rootEntityName, $assocName); + } + + /** + * {@inheritdoc} + */ + protected function getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditionSql = parent::getSelectConditionSQL($criteria, $assoc); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * {@inheritdoc} + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $conditionSql = parent::getSelectConditionCriteriaSQL($criteria); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * @return string + */ + protected function getSelectConditionDiscriminatorValueSQL() + { + $values = array(); + + if ($this->class->discriminatorValue !== null) { // discriminators can be 0 + $values[] = $this->conn->quote($this->class->discriminatorValue); + } + + $discrValues = array_flip($this->class->discriminatorMap); + + foreach ($this->class->subClasses as $subclassName) { + $values[] = $this->conn->quote($discrValues[$subclassName]); + } + + $values = implode(', ', $values); + $discColumn = $this->class->discriminatorColumn['name']; + $tableAlias = $this->getSQLTableAlias($this->class->name); + + return $tableAlias . '.' . $discColumn . ' IN (' . $values . ')'; + } + + /** + * {@inheritdoc} + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + // Ensure that the filters are applied to the root entity of the inheritance tree + $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); + // we don't care about the $targetTableAlias, in a STI there is only one table. + + return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php new file mode 100644 index 00000000..b06358a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlExpressionVisitor.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Visit Expressions and generate SQL WHERE conditions from them. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class SqlExpressionVisitor extends ExpressionVisitor +{ + /** + * @var \Doctrine\ORM\Persisters\BasicEntityPersister + */ + private $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @param \Doctrine\ORM\Persisters\BasicEntityPersister $persister + */ + public function __construct(BasicEntityPersister $persister, ClassMetadata $classMetadata) + { + $this->persister = $persister; + $this->classMetadata = $classMetadata; + } + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + if (isset($this->classMetadata->associationMappings[$field]) && + ! is_object($value) && + ! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) { + + throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field); + } + + return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator()); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return '(' . implode(' AND ', $expressionList) . ')'; + + case CompositeExpression::TYPE_OR: + return '(' . implode(' OR ', $expressionList) . ')'; + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return '?'; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php new file mode 100644 index 00000000..0e680ad0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Extract the values from a criteria/expression + * + * @author Benjamin Eberlei + */ +class SqlValueVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private $values = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $value = $this->getValueFromComparison($comparison); + $field = $comparison->getField(); + $operator = $comparison->getOperator(); + + if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) { + return; + } else if ($operator === Comparison::NEQ && $value === null) { + return; + } + + $this->values[] = $value; + $this->types[] = array($field, $value); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + foreach ($expr->getExpressionList() as $child) { + $this->dispatch($child); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return; + } + + /** + * Returns the Parameters and Types necessary for matching the last visited expression. + * + * @return array + */ + public function getParamsAndTypes() + { + return array($this->values, $this->types); + } + + /** + * Returns the value from a Comparison. In case of a CONTAINS comparison, + * the value is wrapped in %-signs, because it will be used in a LIKE clause. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * @return mixed + */ + protected function getValueFromComparison(Comparison $comparison) + { + $value = $comparison->getValue()->getValue(); + + return $comparison->getOperator() == Comparison::CONTAINS + ? "%{$value}%" + : $value; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php new file mode 100644 index 00000000..d27dcc9f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +class UnionSubclassPersister extends BasicEntityPersister +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php new file mode 100644 index 00000000..2bc93bff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Pessimistic Lock Exception + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class PessimisticLockException extends ORMException +{ + /** + * @return PessimisticLockException + */ + public static function lockFailed() + { + return new self("The pessimistic lock failed."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php new file mode 100644 index 00000000..9b2e2cdf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\Autoloader as BaseAutoloader; + +/** + * @deprecated use \Doctrine\Common\Proxy\Autoloader instead + */ +class Autoloader extends BaseAutoloader +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php new file mode 100644 index 00000000..f478d490 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.0 + */ +interface Proxy extends BaseProxy +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php new file mode 100644 index 00000000..dfac9a3e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -0,0 +1,218 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\AbstractProxyFactory; +use Doctrine\Common\Proxy\ProxyDefinition; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Proxy\Proxy as BaseProxy; +use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\ORM\ORMInvalidArgumentException; +use Doctrine\ORM\Persisters\BasicEntityPersister; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityNotFoundException; + +/** + * This factory is used to create proxy objects for entities at runtime. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Marco Pivetta + * @since 2.0 + */ +class ProxyFactory extends AbstractProxyFactory +{ + /** + * @var \Doctrine\ORM\EntityManager The EntityManager this factory is bound to. + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters + */ + private $uow; + + /** + * @var string + */ + private $proxyNs; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em The EntityManager the new factory works for. + * @param string $proxyDir The directory to use for the proxy classes. It must exist. + * @param string $proxyNs The namespace to use for the proxy classes. + * @param boolean $autoGenerate Whether to automatically generate proxy classes. + */ + public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false) + { + $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs); + + $proxyGenerator->setPlaceholder('baseProxyInterface', 'Doctrine\ORM\Proxy\Proxy'); + parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate); + + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->proxyNs = $proxyNs; + + } + + /** + * {@inheritDoc} + */ + protected function skipClass(ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract(); + } + + /** + * {@inheritDoc} + */ + protected function createProxyDefinition($className) + { + $classMetadata = $this->em->getClassMetadata($className); + $entityPersister = $this->uow->getEntityPersister($className); + + return new ProxyDefinition( + ClassUtils::generateProxyClassName($className, $this->proxyNs), + $classMetadata->getIdentifierFieldNames(), + $classMetadata->getReflectionProperties(), + $this->createInitializer($classMetadata, $entityPersister), + $this->createCloner($classMetadata, $entityPersister) + ); + } + + /** + * Creates a closure capable of initializing a proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\BasicEntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createInitializer(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) + { + if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if (!isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + $proxy->__wakeup(); + + if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw new EntityNotFoundException(); + } + }; + } + + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if (!isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + + if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw new EntityNotFoundException(); + } + }; + } + + /** + * Creates a closure capable of finalizing state a cloned proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\BasicEntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createCloner(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) + { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + if ($proxy->__isInitialized()) { + return; + } + + $proxy->__setInitialized(true); + $proxy->__setInitializer(null); + $class = $entityPersister->getClassMetadata(); + $original = $entityPersister->load($classMetadata->getIdentifierValues($proxy)); + + if (null === $original) { + throw new EntityNotFoundException(); + } + + foreach ($class->getReflectionClass()->getProperties() as $reflectionProperty) { + $propertyName = $reflectionProperty->getName(); + + if ($class->hasField($propertyName) || $class->hasAssociation($propertyName)) { + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original)); + } + } + }; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php new file mode 100644 index 00000000..107055ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php @@ -0,0 +1,662 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\DBAL\LockMode; + +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\ParserResult; +use Doctrine\ORM\Query\QueryException; + +/** + * A Query object represents a DQL query. + * + * @since 1.0 + * @author Guilherme Blanco + * @author Konsta Vesterinen + * @author Roman Borschel + */ +final class Query extends AbstractQuery +{ + /** + * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts. + */ + const STATE_CLEAN = 1; + + /** + * A query object is in state DIRTY when it has DQL parts that have not yet been + * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart + * is called. + */ + const STATE_DIRTY = 2; + + /* Query HINTS */ + + /** + * The refresh hint turns any query into a refresh query with the result that + * any local changes in entities are overridden with the fetched values. + * + * @var string + */ + const HINT_REFRESH = 'doctrine.refresh'; + + /** + * Internal hint: is set to the proxy entity that is currently triggered for loading + * + * @var string + */ + const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity'; + + /** + * The forcePartialLoad query hint forces a particular query to return + * partial objects. + * + * @var string + * @todo Rename: HINT_OPTIMIZE + */ + const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad'; + + /** + * The includeMetaColumns query hint causes meta columns like foreign keys and + * discriminator columns to be selected and returned as part of the query result. + * + * This hint does only apply to non-object queries. + * + * @var string + */ + const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; + + /** + * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and + * are iterated and executed after the DQL has been parsed into an AST. + * + * @var string + */ + const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers'; + + /** + * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker + * and is used for generating the target SQL from any DQL AST tree. + * + * @var string + */ + const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker'; + + //const HINT_READ_ONLY = 'doctrine.readOnly'; + + /** + * @var string + */ + const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; + + /** + * @var string + */ + const HINT_LOCK_MODE = 'doctrine.lockMode'; + + /** + * The current state of this query. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * Cached DQL query. + * + * @var string + */ + private $_dql = null; + + /** + * The parser result that holds DQL => SQL information. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The first result to return (the "offset"). + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to return (the "limit"). + * + * @var integer + */ + private $_maxResults = null; + + /** + * The cache driver used for caching queries. + * + * @var \Doctrine\Common\Cache\Cache|null + */ + private $_queryCache; + + /** + * Whether or not expire the query cache. + * + * @var boolean + */ + private $_expireQueryCache = false; + + /** + * The query cache lifetime. + * + * @var int + */ + private $_queryCacheTTL; + + /** + * Whether to use a query cache, if available. Defaults to TRUE. + * + * @var boolean + */ + private $_useQueryCache = true; + + /** + * Initializes a new Query instance. + * + * @param \Doctrine\ORM\EntityManager $entityManager + */ + /*public function __construct(EntityManager $entityManager) + { + parent::__construct($entityManager); + }*/ + + /** + * Gets the SQL query/queries that correspond to this DQL query. + * + * @return mixed The built sql query or an array of all sql queries. + * + * @override + */ + public function getSQL() + { + return $this->_parse()->getSQLExecutor()->getSQLStatements(); + } + + /** + * Returns the corresponding AST for this DQL query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + $parser = new Parser($this); + + return $parser->getAST(); + } + + /** + * Parses the DQL query, if necessary, and stores the parser result. + * + * Note: Populates $this->_parserResult as a side-effect. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + private function _parse() + { + // Return previous parser result if the query and the filter collection are both clean + if ($this->_state === self::STATE_CLEAN && $this->_em->isFiltersStateClean()) { + return $this->_parserResult; + } + + $this->_state = self::STATE_CLEAN; + + // Check query cache. + if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) { + $parser = new Parser($this); + + $this->_parserResult = $parser->parse(); + + return $this->_parserResult; + } + + $hash = $this->_getQueryCacheId(); + $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); + + if ($cached instanceof ParserResult) { + // Cache hit. + $this->_parserResult = $cached; + + return $this->_parserResult; + } + + // Cache miss. + $parser = new Parser($this); + + $this->_parserResult = $parser->parse(); + + $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL); + + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $executor = $this->_parse()->getSqlExecutor(); + + if ($this->_queryCacheProfile) { + $executor->setQueryCacheProfile($this->_queryCacheProfile); + } + + // Prepare parameters + $paramMappings = $this->_parserResult->getParameterMappings(); + + if (count($paramMappings) != count($this->parameters)) { + throw QueryException::invalidParameterNumber(); + } + + list($sqlParams, $types) = $this->processParameterMappings($paramMappings); + + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + + return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + } + + /** + * Processes query parameter mappings. + * + * @param array $paramMappings + * + * @return array + * + * @throws Query\QueryException + */ + private function processParameterMappings($paramMappings) + { + $sqlParams = array(); + $types = array(); + + foreach ($this->parameters as $parameter) { + $key = $parameter->getName(); + + if ( ! isset($paramMappings[$key])) { + throw QueryException::unknownParameter($key); + } + + $value = $this->processParameterValue($parameter->getValue()); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : Query\ParameterTypeInferer::inferType($value); + + foreach ($paramMappings[$key] as $position) { + $types[$position] = $type; + } + + $sqlPositions = $paramMappings[$key]; + + // optimized multi value sql positions away for now, + // they are not allowed in DQL anyways. + $value = array($value); + $countValue = count($value); + + for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) { + $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)]; + } + } + + if (count($sqlParams) != count($types)) { + throw QueryException::parameterTypeMismatch(); + } + + if ($sqlParams) { + ksort($sqlParams); + $sqlParams = array_values($sqlParams); + + ksort($types); + $types = array_values($types); + } + + return array($sqlParams, $types); + } + + /** + * Defines a cache driver to be used for caching queries. + * + * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver. + * + * @return Query This query instance. + */ + public function setQueryCacheDriver($queryCache) + { + $this->_queryCache = $queryCache; + + return $this; + } + + /** + * Defines whether the query should make use of a query cache, if available. + * + * @param boolean $bool + * + * @return Query This query instance. + */ + public function useQueryCache($bool) + { + $this->_useQueryCache = $bool; + + return $this; + } + + /** + * Returns the cache driver used for query caching. + * + * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if + * this Query does not use query caching. + */ + public function getQueryCacheDriver() + { + if ($this->_queryCache) { + return $this->_queryCache; + } + + return $this->_em->getConfiguration()->getQueryCacheImpl(); + } + + /** + * Defines how long the query cache will be active before expire. + * + * @param integer $timeToLive How long the cache entry is valid. + * + * @return Query This query instance. + */ + public function setQueryCacheLifetime($timeToLive) + { + if ($timeToLive !== null) { + $timeToLive = (int) $timeToLive; + } + + $this->_queryCacheTTL = $timeToLive; + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @return int + */ + public function getQueryCacheLifetime() + { + return $this->_queryCacheTTL; + } + + /** + * Defines if the query cache is active or not. + * + * @param boolean $expire Whether or not to force query cache expiration. + * + * @return Query This query instance. + */ + public function expireQueryCache($expire = true) + { + $this->_expireQueryCache = $expire; + + return $this; + } + + /** + * Retrieves if the query cache is active or not. + * + * @return bool + */ + public function getExpireQueryCache() + { + return $this->_expireQueryCache; + } + + /** + * @override + */ + public function free() + { + parent::free(); + + $this->_dql = null; + $this->_state = self::STATE_CLEAN; + } + + /** + * Sets a DQL query string. + * + * @param string $dqlQuery DQL Query. + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setDQL($dqlQuery) + { + if ($dqlQuery !== null) { + $this->_dql = $dqlQuery; + $this->_state = self::STATE_DIRTY; + } + + return $this; + } + + /** + * Returns the DQL query that is represented by this query object. + * + * @return string DQL query. + */ + public function getDQL() + { + return $this->_dql; + } + + /** + * Returns the state of this query object + * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL + * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. + * + * @see AbstractQuery::STATE_CLEAN + * @see AbstractQuery::STATE_DIRTY + * + * @return integer The query state. + */ + public function getState() + { + return $this->_state; + } + + /** + * Method to check if an arbitrary piece of DQL exists + * + * @param string $dql Arbitrary piece of DQL to check for. + * + * @return boolean + */ + public function contains($dql) + { + return stripos($this->getDQL(), $dql) === false ? false : true; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return Query This query object. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this query. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults + * + * @return Query This query object. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterated over the result. + * + * @param ArrayCollection|array|null $parameters The query parameters. + * @param integer $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT) + { + $this->setHint(self::HINT_INTERNAL_ITERATION, true); + + return parent::iterate($parameters, $hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function setHint($name, $value) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHint($name, $value); + } + + /** + * {@inheritdoc} + */ + public function setHydrationMode($hydrationMode) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHydrationMode($hydrationMode); + } + + /** + * Set the lock mode for this Query. + * + * @see \Doctrine\DBAL\LockMode + * + * @param int $lockMode + * + * @return Query + * + * @throws TransactionRequiredException + */ + public function setLockMode($lockMode) + { + if (in_array($lockMode, array(LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE))) { + if ( ! $this->_em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + } + + $this->setHint(self::HINT_LOCK_MODE, $lockMode); + + return $this; + } + + /** + * Get the current lock mode for this query. + * + * @return int + */ + public function getLockMode() + { + $lockMode = $this->getHint(self::HINT_LOCK_MODE); + + if ( ! $lockMode) { + return LockMode::NONE; + } + + return $lockMode; + } + + /** + * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. + * + * The query cache + * + * @return string + */ + protected function _getQueryCacheId() + { + ksort($this->_hints); + + return md5( + $this->getDql() . var_export($this->_hints, true) . + ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults . + '&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT' + ); + } + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + parent::__clone(); + + $this->_state = self::STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php new file mode 100644 index 00000000..b8f931b4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +use Doctrine\ORM\Query\QueryException; + +/** + * Base exception class for AST exceptions. + */ +class ASTException extends QueryException +{ + /** + * @param Node $node + * + * @return ASTException + */ + public static function noDispatchForNode($node) + { + return new self("Double-dispatch for node " . get_class($node) . " is not supported."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php new file mode 100644 index 00000000..0966d902 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of AggregateExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AggregateExpression extends Node +{ + /** + * @var string + */ + public $functionName; + + /** + * @var PathExpression|SimpleArithmeticExpression + */ + public $pathExpression; + + /** + * Some aggregate expressions support distinct, eg COUNT. + * + * @var bool + */ + public $isDistinct = false; + + /** + * @param string $functionName + * @param PathExpression|SimpleArithmeticExpression $pathExpression + * @param bool $isDistinct + */ + public function __construct($functionName, $pathExpression, $isDistinct) + { + $this->functionName = $functionName; + $this->pathExpression = $pathExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkAggregateExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php new file mode 100644 index 00000000..b586cba3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticExpression extends Node +{ + /** + * @var SimpleArithmeticExpression|null + */ + public $simpleArithmeticExpression; + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @return bool + */ + public function isSimpleArithmeticExpression() + { + return (bool) $this->simpleArithmeticExpression; + } + + /** + * @return bool + */ + public function isSubselect() + { + return (bool) $this->subselect; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkArithmeticExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php new file mode 100644 index 00000000..3120466f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticFactor extends Node +{ + /** + * @var mixed + */ + public $arithmeticPrimary; + + /** + * NULL represents no sign, TRUE means positive and FALSE means negative sign. + * + * @var null|boolean + */ + public $sign; + + /** + * @param mixed $arithmeticPrimary + * @param null|bool $sign + */ + public function __construct($arithmeticPrimary, $sign = null) + { + $this->arithmeticPrimary = $arithmeticPrimary; + $this->sign = $sign; + } + + /** + * @return bool + */ + public function isPositiveSigned() + { + return $this->sign === true; + } + + /** + * @return bool + */ + public function isNegativeSigned() + { + return $this->sign === false; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticFactor($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php new file mode 100644 index 00000000..e08ae7fb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticTerm extends Node +{ + /** + * @var array + */ + public $arithmeticFactors; + + /** + * @param array $arithmeticFactors + */ + public function __construct(array $arithmeticFactors) + { + $this->arithmeticFactors = $arithmeticFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticTerm($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php new file mode 100644 index 00000000..1e31fd1a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of BetweenExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class BetweenExpression extends Node +{ + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var ArithmeticExpression + */ + public $leftBetweenExpression; + + /** + * @var ArithmeticExpression + */ + public $rightBetweenExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param ArithmeticExpression $expr + * @param ArithmeticExpression $leftExpr + * @param ArithmeticExpression $rightExpr + */ + public function __construct($expr, $leftExpr, $rightExpr) + { + $this->expression = $expr; + $this->leftBetweenExpression = $leftExpr; + $this->rightBetweenExpression = $rightExpr; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkBetweenExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php new file mode 100644 index 00000000..194f9ace --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CoalesceExpression extends Node +{ + /** + * @var array + */ + public $scalarExpressions = array(); + + /** + * @param array $scalarExpressions + */ + public function __construct(array $scalarExpressions) + { + $this->scalarExpressions = $scalarExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkCoalesceExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php new file mode 100644 index 00000000..70989a26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CollectionMemberExpression extends Node +{ + public $entityExpression; + + /** + * @var PathExpression + */ + public $collectionValuedPathExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param mixed $entityExpr + * @param PathExpression $collValuedPathExpr + */ + public function __construct($entityExpr, $collValuedPathExpr) + { + $this->entityExpression = $entityExpr; + $this->collectionValuedPathExpression = $collValuedPathExpr; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkCollectionMemberExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php new file mode 100644 index 00000000..05583920 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) | + * StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) | + * BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) | + * EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) | + * DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) | + * EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ComparisonExpression extends Node +{ + /** + * @var Node + */ + public $leftExpression; + + /** + * @var Node + */ + public $rightExpression; + + /** + * @var string + */ + public $operator; + + /** + * @param Node $leftExpr + * @param string $operator + * @param Node $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpression = $leftExpr; + $this->rightExpression = $rightExpr; + $this->operator = $operator; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php new file mode 100644 index 00000000..62c7b2bc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalExpression extends Node +{ + /** + * @var array + */ + public $conditionalTerms = array(); + + /** + * @param array $conditionalTerms + */ + public function __construct(array $conditionalTerms) + { + $this->conditionalTerms = $conditionalTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php new file mode 100644 index 00000000..7c89faa4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalFactor extends Node +{ + /** + * @var bool + */ + public $not = false; + + /** + * @var ConditionalPrimary + */ + public $conditionalPrimary; + + /** + * @param ConditionalPrimary $conditionalPrimary + */ + public function __construct($conditionalPrimary) + { + $this->conditionalPrimary = $conditionalPrimary; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalFactor($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php new file mode 100644 index 00000000..1eed41dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalPrimary extends Node +{ + /** + * @var Node|null + */ + public $simpleConditionalExpression; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression; + + /** + * @return bool + */ + public function isSimpleConditionalExpression() + { + return (bool) $this->simpleConditionalExpression; + } + + /** + * @return bool + */ + public function isConditionalExpression() + { + return (bool) $this->conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalPrimary($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php new file mode 100644 index 00000000..bb92db11 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php @@ -0,0 +1,52 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalTerm extends Node +{ + /** + * @var array + */ + public $conditionalFactors = array(); + + /** + * @param array $conditionalFactors + */ + public function __construct(array $conditionalFactors) + { + $this->conditionalFactors = $conditionalFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalTerm($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php new file mode 100644 index 00000000..8ca35c67 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @param string $abstractSchemaName + */ + public function __construct($abstractSchemaName) + { + $this->abstractSchemaName = $abstractSchemaName; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php new file mode 100644 index 00000000..da6859b8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteStatement = DeleteClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteStatement extends Node +{ + /** + * @var DeleteClause + */ + public $deleteClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param DeleteClause $deleteClause + */ + public function __construct($deleteClause) + { + $this->deleteClause = $deleteClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php new file mode 100644 index 00000000..bd978af0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EmptyCollectionComparisonExpression extends Node +{ + /** + * @var PathExpression + */ + public $expression; + + /** + * @var bool + */ + public $not; + + /** + * @param PathExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkEmptyCollectionComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php new file mode 100644 index 00000000..c53a1077 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ExistsExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkExistsExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php new file mode 100644 index 00000000..b1d8dfe6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class FromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFromClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php new file mode 100644 index 00000000..8fade4c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "ABS" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class AbsFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php new file mode 100644 index 00000000..0e5edd55 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitAndFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitAndComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php new file mode 100644 index 00000000..1d9c2df0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitOrFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitOrComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php new file mode 100644 index 00000000..90ca9b35 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ConcatFunction extends FunctionNode +{ + public $firstStringPrimary; + + public $secondStringPrimary; + + public $concatExpressions = array(); + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + $args = array(); + + foreach ($this->concatExpressions as $expression) { + $args[] = $sqlWalker->walkStringPrimary($expression); + } + + return call_user_func_array(array($platform,'getConcatExpression'), $args); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->firstStringPrimary; + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->secondStringPrimary; + + while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $this->concatExpressions[] = $parser->StringPrimary(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php new file mode 100644 index 00000000..8e8ce1c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_DATE" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentDateFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php new file mode 100644 index 00000000..4bab247c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIME" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimeFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php new file mode 100644 index 00000000..8a9b4185 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIMESTAMP" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimestampFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php new file mode 100644 index 00000000..1c3817fe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateAddFunction extends FunctionNode +{ + public $firstDateExpression = null; + public $intervalExpression = null; + public $unit = null; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_ADD() only supports units of type day and month.' + ); + } + } + + /** + * @override + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->intervalExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->unit = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php new file mode 100644 index 00000000..a33c2d06 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +/** + * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateDiffFunction extends FunctionNode +{ + public $date1; + public $date2; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( + $this->date1->dispatch($sqlWalker), + $this->date2->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->date1 = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->date2 = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php new file mode 100644 index 00000000..35add0ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD(date1, interval, unit)" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateSubFunction extends DateAddFunction +{ + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_SUB() only supports units of type day and month.' + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php new file mode 100644 index 00000000..2f33c9da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\AST\Node; + +/** + * Abstract Function Node. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +abstract class FunctionNode extends Node +{ + /** + * @var string + */ + public $name; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker); + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFunction($this); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + abstract public function parse(\Doctrine\ORM\Query\Parser $parser); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php new file mode 100644 index 00000000..5f51a904 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class IdentityFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + public $pathExpression; + + /** + * @var string + */ + public $fieldMapping; + + /** + * {@inheritdoc} + */ + public function getSql(SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $dqlAlias = $this->pathExpression->identificationVariable; + $assocField = $this->pathExpression->field; + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $targetEntity = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $joinColumn = reset($assoc['joinColumns']); + + if ($this->fieldMapping !== null) { + if ( ! isset($targetEntity->fieldMappings[$this->fieldMapping])) { + throw new QueryException(sprintf('Undefined reference field mapping "%s"', $this->fieldMapping)); + } + + $field = $targetEntity->fieldMappings[$this->fieldMapping]; + $joinColumn = null; + + foreach ($assoc['joinColumns'] as $mapping) { + + if($mapping['referencedColumnName'] === $field['columnName']) { + $joinColumn = $mapping; + + break; + } + } + + if ($joinColumn === null) { + throw new QueryException(sprintf('Unable to resolve the reference field mapping "%s"', $this->fieldMapping)); + } + } + + $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); + + return $tableAlias . '.' . $columnName; + } + + /** + * {@inheritdoc} + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->pathExpression = $parser->SingleValuedAssociationPathExpression(); + + if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_STRING); + + $this->fieldMapping = $parser->getLexer()->token['value']; + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php new file mode 100644 index 00000000..deb3066e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LENGTH" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LengthFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php new file mode 100644 index 00000000..cba45bc1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LocateFunction extends FunctionNode +{ + public $firstStringPrimary; + public $secondStringPrimary; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|bool + */ + public $simpleArithmeticExpression = false; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + + return $sqlWalker->getConnection()->getDatabasePlatform()->getLocateExpression( + $sqlWalker->walkStringPrimary($this->secondStringPrimary), // its the other way around in platform + $sqlWalker->walkStringPrimary($this->firstStringPrimary), + (($this->simpleArithmeticExpression) + ? $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + : false + ) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php new file mode 100644 index 00000000..ffc86b6f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOWER" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LowerFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php new file mode 100644 index 00000000..cf4cf0c9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ModFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $secondSimpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php new file mode 100644 index 00000000..7970508f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SIZE" "(" CollectionValuedPathExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SizeFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + public $collectionPathExpression; + + /** + * @override + * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient). + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $dqlAlias = $this->collectionPathExpression->identificationVariable; + $assocField = $this->collectionPathExpression->field; + + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $sql = 'SELECT COUNT(*) FROM '; + + if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $quoteStrategy->getTableName($targetClass, $platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + + $first = true; + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sql .= $targetTableAlias . '.' . $sourceColumn + . ' = ' + . $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform); + } + } else { // many-to-many + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] + ? $joinTable['joinColumns'] + : $joinTable['inverseJoinColumns']; + + $first = true; + + foreach ($joinColumns as $joinColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sourceColumnName = $quoteStrategy->getColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], $class, $platform + ); + + $sql .= $joinTableAlias . '.' . $joinColumn['name'] + . ' = ' + . $sourceTableAlias . '.' . $sourceColumnName; + } + } + + return '(' . $sql . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->collectionPathExpression = $parser->CollectionValuedPathExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php new file mode 100644 index 00000000..c9f1038c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SQRT" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SqrtFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getSqrtExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php new file mode 100644 index 00000000..ee80c06a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SubstringFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|null + */ + public $secondSimpleArithmeticExpression = null; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $optionalSecondSimpleArithmeticExpression = null; + if ($this->secondSimpleArithmeticExpression !== null) { + $optionalSecondSimpleArithmeticExpression = $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression); + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $optionalSecondSimpleArithmeticExpression + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php new file mode 100644 index 00000000..69a45d2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -0,0 +1,115 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class TrimFunction extends FunctionNode +{ + /** + * @var bool + */ + public $leading; + + /** + * @var bool + */ + public $trailing; + + /** + * @var bool + */ + public $both; + + /** + * @var bool + */ + public $trimChar = false; + + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $pos = AbstractPlatform::TRIM_UNSPECIFIED; + if ($this->leading) { + $pos = AbstractPlatform::TRIM_LEADING; + } else if ($this->trailing) { + $pos = AbstractPlatform::TRIM_TRAILING; + } else if ($this->both) { + $pos = AbstractPlatform::TRIM_BOTH; + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $pos, + ($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + if (strcasecmp('leading', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_LEADING); + $this->leading = true; + } else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_TRAILING); + $this->trailing = true; + } else if (strcasecmp('both', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_BOTH); + $this->both = true; + } + + if ($lexer->isNextToken(Lexer::T_STRING)) { + $parser->match(Lexer::T_STRING); + $this->trimChar = $lexer->token['value']; + } + + if ($this->leading || $this->trailing || $this->both || $this->trimChar) { + $parser->match(Lexer::T_FROM); + } + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php new file mode 100644 index 00000000..888009a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "UPPER" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class UpperFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php new file mode 100644 index 00000000..a74eed59 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GeneralCaseExpression extends Node +{ + /** + * @var array + */ + public $whenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param array $whenClauses + * @param mixed $elseScalarExpression + */ + public function __construct(array $whenClauses, $elseScalarExpression) + { + $this->whenClauses = $whenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGeneralCaseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php new file mode 100644 index 00000000..c05fa586 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of GroupByClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupByClause extends Node +{ + /** + * @var array + */ + public $groupByItems = array(); + + /** + * @param array $groupByItems + */ + public function __construct(array $groupByItems) + { + $this->groupByItems = $groupByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGroupByClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php new file mode 100644 index 00000000..1d369fff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of HavingClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class HavingClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkHavingClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php new file mode 100644 index 00000000..a8f7f6d3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IdentificationVariableDeclaration extends Node +{ + /** + * @var RangeVariableDeclaration|null + */ + public $rangeVariableDeclaration = null; + + /** + * @var IndexBy|null + */ + public $indexBy = null; + + /** + * @var array + */ + public $joins = array(); + + /** + * @param RangeVariableDeclaration|null $rangeVariableDecl + * @param IndexBy|null $indexBy + * @param array $joins + */ + public function __construct($rangeVariableDecl, $indexBy, array $joins) + { + $this->rangeVariableDeclaration = $rangeVariableDecl; + $this->indexBy = $indexBy; + $this->joins = $joins; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIdentificationVariableDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php new file mode 100644 index 00000000..9d0a8b54 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php @@ -0,0 +1,67 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var array + */ + public $literals = array(); + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @param ArithmeticExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php new file mode 100644 index 00000000..c7874b70 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IndexBy extends Node +{ + /** + * @var PathExpression + */ + public $simpleStateFieldPathExpression = null; + + /** + * @param PathExpression $simpleStateFieldPathExpression + */ + public function __construct($simpleStateFieldPathExpression) + { + $this->simpleStateFieldPathExpression = $simpleStateFieldPathExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIndexBy($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php new file mode 100644 index 00000000..cf50140b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of InputParameter. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InputParameter extends Node +{ + /** + * @var bool + */ + public $isNamed; + + /** + * @var string + */ + public $name; + + /** + * @param string $value + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function __construct($value) + { + if (strlen($value) == 1) { + throw \Doctrine\ORM\Query\QueryException::invalidParameterFormat($value); + } + + $param = substr($value, 1); + $this->isNamed = ! is_numeric($param); + $this->name = $param; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkInputParameter($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php new file mode 100644 index 00000000..c1fd65b8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InstanceOfExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $value; + + /** + * @param string $identVariable + */ + public function __construct($identVariable) + { + $this->identificationVariable = $identVariable; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInstanceOfExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php new file mode 100644 index 00000000..5c203aa0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression + * ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join extends Node +{ + const JOIN_TYPE_LEFT = 1; + const JOIN_TYPE_LEFTOUTER = 2; + const JOIN_TYPE_INNER = 3; + + /** + * @var int + */ + public $joinType = self::JOIN_TYPE_INNER; + + /** + * @var Node|null + */ + public $joinAssociationDeclaration = null; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression = null; + + /** + * @param int $joinType + * @param Node $joinAssociationDeclaration + */ + public function __construct($joinType, $joinAssociationDeclaration) + { + $this->joinType = $joinType; + $this->joinAssociationDeclaration = $joinAssociationDeclaration; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoin($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php new file mode 100644 index 00000000..a33900a6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class JoinAssociationDeclaration extends Node +{ + /** + * @var JoinAssociationPathExpression + */ + public $joinAssociationPathExpression; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var IndexBy|null + */ + public $indexBy; + + /** + * @param JoinAssociationPathExpression $joinAssociationPathExpression + * @param string $aliasIdentificationVariable + * @param IndexBy|null $indexBy + */ + public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy) + { + $this->joinAssociationPathExpression = $joinAssociationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + $this->indexBy = $indexBy; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoinAssociationDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php new file mode 100644 index 00000000..946bbb15 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class JoinAssociationPathExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string + */ + public $associationField; + + /** + * @param string $identificationVariable + * @param string $associationField + */ + public function __construct($identificationVariable, $associationField) + { + $this->identificationVariable = $identificationVariable; + $this->associationField = $associationField; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php new file mode 100644 index 00000000..1c67cb9c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.3 + * @author Alexander + */ +class JoinClassPathExpression extends Node +{ + /** + * @var mixed + */ + public $abstractSchemaName; + + /** + * @var mixed + */ + public $aliasIdentificationVariable; + + /** + * @param mixed $abstractSchemaName + * @param mixed $aliasIdentificationVar + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkJoinPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php new file mode 100644 index 00000000..e320c51c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php @@ -0,0 +1,71 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class LikeExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $stringExpression; + + /** + * @var InputParameter + */ + public $stringPattern; + + /** + * @var Literal|null + */ + public $escapeChar; + + /** + * @param Node $stringExpression + * @param InputParameter $stringPattern + * @param Literal|null $escapeChar + */ + public function __construct($stringExpression, $stringPattern, $escapeChar = null) + { + $this->stringExpression = $stringExpression; + $this->stringPattern = $stringPattern; + $this->escapeChar = $escapeChar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkLikeExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php new file mode 100644 index 00000000..43d71add --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class Literal extends Node +{ + const STRING = 1; + const BOOLEAN = 2; + const NUMERIC = 3; + + /** + * @var int + */ + public $type; + + /** + * @var mixed + */ + public $value; + + /** + * @param int $type + * @param mixed $value + */ + public function __construct($type, $value) + { + $this->type = $type; + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkLiteral($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php new file mode 100644 index 00000000..65b94ac6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Fabio B. Silva + */ +class NewObjectExpression extends Node +{ + /** + * @var string + */ + public $className; + + /** + * @var array + */ + public $args; + + /** + * @param string $className + * @param array $args + */ + public function __construct($className, array $args) + { + $this->className = $className; + $this->args = $args; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNewObject($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php new file mode 100644 index 00000000..a257dc2d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Abstract class of an AST node. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Node +{ + /** + * Double-dispatch method, supposed to dispatch back to the walker. + * + * Implementation is not mandatory for all nodes. + * + * @param \Doctrine\ORM\Query\SqlWalker $walker + * + * @return string + * + * @throws ASTException + */ + public function dispatch($walker) + { + throw ASTException::noDispatchForNode($this); + } + + /** + * Dumps the AST Node into a string representation for information purpose only. + * + * @return string + */ + public function __toString() + { + return $this->dump($this); + } + + /** + * @param object $obj + * + * @return string + */ + public function dump($obj) + { + static $ident = 0; + + $str = ''; + + if ($obj instanceof Node) { + $str .= get_class($obj) . '(' . PHP_EOL; + $props = get_object_vars($obj); + + foreach ($props as $name => $prop) { + $ident += 4; + $str .= str_repeat(' ', $ident) . '"' . $name . '": ' + . $this->dump($prop) . ',' . PHP_EOL; + $ident -= 4; + } + + $str .= str_repeat(' ', $ident) . ')'; + } else if (is_array($obj)) { + $ident += 4; + $str .= 'array('; + $some = false; + + foreach ($obj as $k => $v) { + $str .= PHP_EOL . str_repeat(' ', $ident) . '"' + . $k . '" => ' . $this->dump($v) . ','; + $some = true; + } + + $ident -= 4; + $str .= ($some ? PHP_EOL . str_repeat(' ', $ident) : '') . ')'; + } else if (is_object($obj)) { + $str .= 'instanceof(' . get_class($obj) . ')'; + } else { + $str .= var_export($obj, true); + } + + return $str; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php new file mode 100644 index 00000000..84a19978 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullComparisonExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $expression; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php new file mode 100644 index 00000000..e33bc72b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullIfExpression extends Node +{ + /** + * @var mixed + */ + public $firstExpression; + + /** + * @var mixed + */ + public $secondExpression; + + /** + * @param mixed $firstExpression + * @param mixed $secondExpression + */ + public function __construct($firstExpression, $secondExpression) + { + $this->firstExpression = $firstExpression; + $this->secondExpression = $secondExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullIfExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php new file mode 100644 index 00000000..75d16c73 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByClause extends Node +{ + /** + * @var array + */ + public $orderByItems = array(); + + /** + * @param array $orderByItems + */ + public function __construct(array $orderByItems) + { + $this->orderByItems = $orderByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php new file mode 100644 index 00000000..bf3288a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"] + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByItem extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string + */ + public $type; + + /** + * @param mixed $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * @return bool + */ + public function isAsc() + { + return strtoupper($this->type) == 'ASC'; + } + + /** + * @return bool + */ + public function isDesc() + { + return strtoupper($this->type) == 'DESC'; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php new file mode 100644 index 00000000..f16db0eb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ParenthesisExpression ::= "(" ArithmeticPrimary ")" + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ParenthesisExpression extends Node +{ + /** + * @var \Doctrine\ORM\Query\AST\Node + */ + public $expression; + + /** + * @param \Doctrine\ORM\Query\AST\Node $expression + */ + public function __construct(Node $expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkParenthesisExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php new file mode 100644 index 00000000..e4ffe79b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class PartialObjectExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $partialFieldSet; + + /** + * @param string $identificationVariable + * @param array $partialFieldSet + */ + public function __construct($identificationVariable, array $partialFieldSet) + { + $this->identificationVariable = $identificationVariable; + $this->partialFieldSet = $partialFieldSet; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php new file mode 100644 index 00000000..37674b6f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + * SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class PathExpression extends Node +{ + const TYPE_COLLECTION_VALUED_ASSOCIATION = 2; + const TYPE_SINGLE_VALUED_ASSOCIATION = 4; + const TYPE_STATE_FIELD = 8; + + /** + * @var int + */ + public $type; + + /** + * @var int + */ + public $expectedType; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string|null + */ + public $field; + + /** + * @param int $expectedType + * @param string $identificationVariable + * @param string|null $field + */ + public function __construct($expectedType, $identificationVariable, $field = null) + { + $this->expectedType = $expectedType; + $this->identificationVariable = $identificationVariable; + $this->field = $field; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php new file mode 100644 index 00000000..15be9523 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,81 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QuantifiedExpression extends Node +{ + /** + * @var string + */ + public $type; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * @return bool + */ + public function isAll() + { + return strtoupper($this->type) == 'ALL'; + } + + /** + * @return bool + */ + public function isAny() + { + return strtoupper($this->type) == 'ANY'; + } + + /** + * @return bool + */ + public function isSome() + { + return strtoupper($this->type) == 'SOME'; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkQuantifiedExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php new file mode 100644 index 00000000..72a2ea9a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RangeVariableDeclaration extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @param string $abstractSchemaName + * @param string $aliasIdentificationVar + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkRangeVariableDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php new file mode 100644 index 00000000..1df14367 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct; + + /** + * @var array + */ + public $selectExpressions = array(); + + /** + * @param array $selectExpressions + * @param bool $isDistinct + */ + public function __construct(array $selectExpressions, $isDistinct) + { + $this->isDistinct = $isDistinct; + $this->selectExpressions = $selectExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php new file mode 100644 index 00000000..41870139 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | + * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectExpression extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string|null + */ + public $fieldIdentificationVariable; + + /** + * @var bool + */ + public $hiddenAliasResultVariable; + + /** + * @param mixed $expression + * @param string|null $fieldIdentificationVariable + * @param bool $hiddenAliasResultVariable + */ + public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) + { + $this->expression = $expression; + $this->fieldIdentificationVariable = $fieldIdentificationVariable; + $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php new file mode 100644 index 00000000..d84f7258 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectStatement extends Node +{ + /** + * @var SelectClause + */ + public $selectClause; + + /** + * @var FromClause + */ + public $fromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SelectClause $selectClause + * @param FromClause $fromClause + */ + public function __construct($selectClause, $fromClause) + { + $this->selectClause = $selectClause; + $this->fromClause = $fromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php new file mode 100644 index 00000000..9bd48504 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleArithmeticExpression extends Node +{ + /** + * @var array + */ + public $arithmeticTerms = array(); + + /** + * @param array $arithmeticTerms + */ + public function __construct(array $arithmeticTerms) + { + $this->arithmeticTerms = $arithmeticTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleArithmeticExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php new file mode 100644 index 00000000..5272f396 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleCaseExpression extends Node +{ + /** + * @var PathExpression + */ + public $caseOperand = null; + + /** + * @var array + */ + public $simpleWhenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param PathExpression $caseOperand + * @param array $simpleWhenClauses + * @param mixed $elseScalarExpression + */ + public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) + { + $this->caseOperand = $caseOperand; + $this->simpleWhenClauses = $simpleWhenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleCaseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php new file mode 100644 index 00000000..92361da4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct = false; + + /** + * @var SimpleSelectExpression + */ + public $simpleSelectExpression; + + /** + * @param SimpleSelectExpression $simpleSelectExpression + * @param bool $isDistinct + */ + public function __construct($simpleSelectExpression, $isDistinct) + { + $this->simpleSelectExpression = $simpleSelectExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php new file mode 100644 index 00000000..e556835e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable + * | (AggregateExpression [["AS"] FieldAliasIdentificationVariable]) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectExpression extends Node +{ + /** + * @var Node + */ + public $expression; + + /** + * @var string + */ + public $fieldIdentificationVariable; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php new file mode 100644 index 00000000..4f60881d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleWhenClause extends Node +{ + /** + * @var mixed + */ + public $caseScalarExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param mixed $caseScalarExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseScalarExpression, $thenScalarExpression) + { + $this->caseScalarExpression = $caseScalarExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php new file mode 100644 index 00000000..ce08266f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Subselect extends Node +{ + /** + * @var SimpleSelectClause + */ + public $simpleSelectClause; + + /** + * @var SubselectFromClause + */ + public $subselectFromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SimpleSelectClause $simpleSelectClause + * @param SubselectFromClause $subselectFromClause + */ + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->simpleSelectClause = $simpleSelectClause; + $this->subselectFromClause = $subselectFromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselect($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php new file mode 100644 index 00000000..8d009fcf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SubselectFromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselectFromClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php new file mode 100644 index 00000000..430ed14e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var array + */ + public $updateItems = array(); + + /** + * @param string $abstractSchemaName + * @param array $updateItems + */ + public function __construct($abstractSchemaName, array $updateItems) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->updateItems = $updateItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php new file mode 100644 index 00000000..f1a288ca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateItem extends Node +{ + /** + * @var PathExpression + */ + public $pathExpression; + + /** + * @var InputParameter|ArithmeticExpression|null + */ + public $newValue; + + /** + * @param PathExpression $pathExpression + * @param InputParameter|ArithmeticExpression|null $newValue + */ + public function __construct($pathExpression, $newValue) + { + $this->pathExpression = $pathExpression; + $this->newValue = $newValue; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php new file mode 100644 index 00000000..c578efef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateStatement = UpdateClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateStatement extends Node +{ + /** + * @var UpdateClause + */ + public $updateClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param UpdateClause $updateClause + */ + public function __construct($updateClause) + { + $this->updateClause = $updateClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php new file mode 100644 index 00000000..01c0330f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhenClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $caseConditionExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param ConditionalExpression $caseConditionExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseConditionExpression, $thenScalarExpression) + { + $this->caseConditionExpression = $caseConditionExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php new file mode 100644 index 00000000..e6597752 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhereClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhereClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php new file mode 100644 index 00000000..3d6ed435 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Base class for SQL statement executors. + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @todo Rename: AbstractSQLExecutor + */ +abstract class AbstractSqlExecutor +{ + /** + * @var array + */ + protected $_sqlStatements; + + /** + * @var QueryCacheProfile + */ + protected $queryCacheProfile; + + /** + * Gets the SQL statements that are executed by the executor. + * + * @return array All the SQL update statements. + */ + public function getSqlStatements() + { + return $this->_sqlStatements; + } + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp + * + * @return void + */ + public function setQueryCacheProfile(QueryCacheProfile $qcp) + { + $this->queryCacheProfile = $qcp; + } + + /** + * Executes all sql statements. + * + * @param Connection $conn The database connection that is used to execute the queries. + * @param array $params The parameters. + * @param array $types The parameter types. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + abstract public function execute(Connection $conn, array $params, array $types); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php new file mode 100644 index 00000000..13af5590 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL DELETE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class MultiTableDeleteExecutor extends AbstractSqlExecutor +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName); + $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // Append WHERE clause, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store DELETE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + foreach (array_reverse($classNames) as $className) { + $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform); + $this->_sqlStatements[] = 'DELETE FROM ' . $tableName + . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => \Doctrine\DBAL\Types\Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numDeleted = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + try { + // Insert identifiers + $numDeleted = $conn->executeUpdate($this->_insertSql, $params, $types); + + // Execute DELETE statements + foreach ($this->_sqlStatements as $sql) { + $conn->executeUpdate($sql); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numDeleted; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php new file mode 100644 index 00000000..8ceefe3a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -0,0 +1,206 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\ORM\Query\ParameterTypeInferer; +use Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL UPDATE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @since 2.0 + */ +class MultiTableUpdateExecutor extends AbstractSqlExecutor +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * @var array + */ + private $_sqlParameters = array(); + + /** + * @var int + */ + private $_numParametersInUpdateClause = 0; + + /** + * Initializes a new MultiTableUpdateExecutor. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $updateClause = $AST->updateClause; + $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $updateItems = $updateClause->updateItems; + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store UPDATE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + $i = -1; + + foreach (array_reverse($classNames) as $className) { + $affected = false; + $class = $em->getClassMetadata($className); + $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET '; + + foreach ($updateItems as $updateItem) { + $field = $updateItem->pathExpression->field; + + if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) || + isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) { + $newValue = $updateItem->newValue; + + if ( ! $affected) { + $affected = true; + ++$i; + } else { + $updateSql .= ', '; + } + + $updateSql .= $sqlWalker->walkUpdateItem($updateItem); + + if ($newValue instanceof AST\InputParameter) { + $this->_sqlParameters[$i][] = $newValue->name; + + ++$this->_numParametersInUpdateClause; + } + } + } + + if ($affected) { + $this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + } + + // Append WHERE clause to insertSql, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numUpdated = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + try { + // Insert identifiers. Parameters from the update clause are cut off. + $numUpdated = $conn->executeUpdate( + $this->_insertSql, + array_slice($params, $this->_numParametersInUpdateClause), + array_slice($types, $this->_numParametersInUpdateClause) + ); + + // Execute UPDATE statements + foreach ($this->_sqlStatements as $key => $statement) { + $paramValues = array(); + $paramTypes = array(); + + if (isset($this->_sqlParameters[$key])) { + foreach ($this->_sqlParameters[$key] as $parameterKey => $parameterName) { + $paramValues[] = $params[$parameterKey]; + $paramTypes[] = isset($types[$parameterKey]) ? $types[$parameterKey] : ParameterTypeInferer::inferType($params[$parameterKey]); + } + } + + $conn->executeUpdate($statement, $paramValues, $paramTypes); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numUpdated; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php new file mode 100644 index 00000000..f652fbe8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\SqlWalker; + +/** + * Executor that executes the SQL statement for simple DQL SELECT statements. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +class SingleSelectExecutor extends AbstractSqlExecutor +{ + /** + * @param \Doctrine\ORM\Query\AST\SelectStatement $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + public function __construct(SelectStatement $AST, SqlWalker $sqlWalker) + { + $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php new file mode 100644 index 00000000..695176d5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST; + +/** + * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes + * that are mapped to a single table. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. + */ +class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor +{ + /** + * @param \Doctrine\ORM\Query\AST\Node $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + if ($AST instanceof AST\UpdateStatement) { + $this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST); + } else if ($AST instanceof AST\DeleteStatement) { + $this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeUpdate($this->_sqlStatements, $params, $types); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php new file mode 100644 index 00000000..48fb790b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php @@ -0,0 +1,647 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * This class is used to generate DQL expressions via a set of PHP static functions. + + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: ExpressionBuilder + */ +class Expr +{ + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) AND (u.role = ?2) + * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2')); + * + * @param \Doctrine\ORM\Query\Expr\Comparison | + * \Doctrine\ORM\Query\Expr\Func | + * \Doctrine\ORM\Query\Expr\Orx + * $x Optional clause. Defaults to null, but requires at least one defined when converting to string. + * + * @return Expr\Andx + */ + public function andX($x = null) + { + return new Expr\Andx(func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) OR (u.role = ?2) + * $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2')); + * + * @param mixed $x Optional clause. Defaults to null, but requires + * at least one defined when converting to string. + * + * @return Expr\Orx + */ + public function orX($x = null) + { + return new Expr\Orx(func_get_args()); + } + + /** + * Creates an ASCending order expression. + * + * @param mixed $expr + * + * @return Expr\OrderBy + */ + public function asc($expr) + { + return new Expr\OrderBy($expr, 'ASC'); + } + + /** + * Creates a DESCending order expression. + * + * @param mixed $expr + * + * @return Expr\OrderBy + */ + public function desc($expr) + { + return new Expr\OrderBy($expr, 'DESC'); + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ?1 + * $expr->eq('u.id', '?1'); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function eq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::EQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> ?1 + * $q->where($q->expr()->neq('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function neq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::NEQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ?1 + * $q->where($q->expr()->lt('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function lt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ?1 + * $q->where($q->expr()->lte('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function lte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LTE, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ?1 + * $q->where($q->expr()->gt('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function gt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ?1 + * $q->where($q->expr()->gte('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function gte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GTE, $y); + } + + /** + * Creates an instance of AVG() function, with the given argument. + * + * @param mixed $x Argument to be used in AVG() function. + * + * @return Expr\Func + */ + public function avg($x) + { + return new Expr\Func('AVG', array($x)); + } + + /** + * Creates an instance of MAX() function, with the given argument. + * + * @param mixed $x Argument to be used in MAX() function. + * + * @return Expr\Func + */ + public function max($x) + { + return new Expr\Func('MAX', array($x)); + } + + /** + * Creates an instance of MIN() function, with the given argument. + * + * @param mixed $x Argument to be used in MIN() function. + * + * @return Expr\Func + */ + public function min($x) + { + return new Expr\Func('MIN', array($x)); + } + + /** + * Creates an instance of COUNT() function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT() function. + * + * @return Expr\Func + */ + public function count($x) + { + return new Expr\Func('COUNT', array($x)); + } + + /** + * Creates an instance of COUNT(DISTINCT) function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT(DISTINCT) function. + * + * @return string + */ + public function countDistinct($x) + { + return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')'; + } + + /** + * Creates an instance of EXISTS() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in EXISTS() function. + * + * @return Expr\Func + */ + public function exists($subquery) + { + return new Expr\Func('EXISTS', array($subquery)); + } + + /** + * Creates an instance of ALL() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in ALL() function. + * + * @return Expr\Func + */ + public function all($subquery) + { + return new Expr\Func('ALL', array($subquery)); + } + + /** + * Creates a SOME() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in SOME() function. + * + * @return Expr\Func + */ + public function some($subquery) + { + return new Expr\Func('SOME', array($subquery)); + } + + /** + * Creates an ANY() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in ANY() function. + * + * @return Expr\Func + */ + public function any($subquery) + { + return new Expr\Func('ANY', array($subquery)); + } + + /** + * Creates a negation expression of the given restriction. + * + * @param mixed $restriction Restriction to be used in NOT() function. + * + * @return Expr\Func + */ + public function not($restriction) + { + return new Expr\Func('NOT', array($restriction)); + } + + /** + * Creates an ABS() function expression with the given argument. + * + * @param mixed $x Argument to be used in ABS() function. + * + * @return Expr\Func + */ + public function abs($x) + { + return new Expr\Func('ABS', array($x)); + } + + /** + * Creates a product mathematical expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a * . Example: + * + * [php] + * // u.salary * u.percentAnnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function prod($x, $y) + { + return new Expr\Math($x, '*', $y); + } + + /** + * Creates a difference mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a - . Example: + * + * [php] + * // u.monthlySubscriptionCount - 1 + * $q->expr()->diff('u.monthlySubscriptionCount', '1') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function diff($x, $y) + { + return new Expr\Math($x, '-', $y); + } + + /** + * Creates a sum mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a + . Example: + * + * [php] + * // u.numChildren + 1 + * $q->expr()->diff('u.numChildren', '1') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function sum($x, $y) + { + return new Expr\Math($x, '+', $y); + } + + /** + * Creates a quotient mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a / . Example: + * + * [php] + * // u.total / u.period + * $expr->quot('u.total', 'u.period') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function quot($x, $y) + { + return new Expr\Math($x, '/', $y); + } + + /** + * Creates a SQRT() function expression with the given argument. + * + * @param mixed $x Argument to be used in SQRT() function. + * + * @return Expr\Func + */ + public function sqrt($x) + { + return new Expr\Func('SQRT', array($x)); + } + + /** + * Creates an IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IN() function. + * @param mixed $y Argument to be used in IN() function. + * + * @return Expr\Func + */ + public function in($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' IN', (array) $y); + } + + /** + * Creates a NOT IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by NOT IN() function. + * @param mixed $y Argument to be used in NOT IN() function. + * + * @return Expr\Func + */ + public function notIn($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' NOT IN', (array) $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NULL. + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NOT NULL. + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return Expr\Comparison + */ + public function like($x, $y) + { + return new Expr\Comparison($x, 'LIKE', $y); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return Expr\Comparison + */ + public function notLike($x, $y) + { + return new Expr\Comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a CONCAT() function expression with the given arguments. + * + * @param mixed $x First argument to be used in CONCAT() function. + * @param mixed $y Second argument to be used in CONCAT() function. + * + * @return Expr\Func + */ + public function concat($x, $y) + { + return new Expr\Func('CONCAT', array($x, $y)); + } + + /** + * Creates a SUBSTRING() function expression with the given arguments. + * + * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function. + * @param int $from Initial offset to start cropping string. May accept negative values. + * @param int|null $len Length of crop. May accept negative values. + * + * @return Expr\Func + */ + public function substring($x, $from, $len = null) + { + $args = array($x, $from); + if (null !== $len) { + $args[] = $len; + } + return new Expr\Func('SUBSTRING', $args); + } + + /** + * Creates a LOWER() function expression with the given argument. + * + * @param mixed $x Argument to be used in LOWER() function. + * + * @return Expr\Func A LOWER function expression. + */ + public function lower($x) + { + return new Expr\Func('LOWER', array($x)); + } + + /** + * Creates an UPPER() function expression with the given argument. + * + * @param mixed $x Argument to be used in UPPER() function. + * + * @return Expr\Func An UPPER function expression. + */ + public function upper($x) + { + return new Expr\Func('UPPER', array($x)); + } + + /** + * Creates a LENGTH() function expression with the given argument. + * + * @param mixed $x Argument to be used as argument of LENGTH() function. + * + * @return Expr\Func A LENGTH function expression. + */ + public function length($x) + { + return new Expr\Func('LENGTH', array($x)); + } + + /** + * Creates a literal expression of the given argument. + * + * @param mixed $literal Argument to be converted to literal. + * + * @return Expr\Literal + */ + public function literal($literal) + { + return new Expr\Literal($this->_quoteLiteral($literal)); + } + + /** + * Quotes a literal value, if necessary, according to the DQL syntax. + * + * @param mixed $literal The literal value. + * + * @return string + */ + private function _quoteLiteral($literal) + { + if (is_numeric($literal) && !is_string($literal)) { + return (string) $literal; + } else if (is_bool($literal)) { + return $literal ? "true" : "false"; + } else { + return "'" . str_replace("'", "''", $literal) . "'"; + } + } + + /** + * Creates an instance of BETWEEN() function, with the given argument. + * + * @param mixed $val Valued to be inspected by range values. + * @param integer $x Starting range value to be used in BETWEEN() function. + * @param integer $y End point value to be used in BETWEEN() function. + * + * @return Expr\Func A BETWEEN expression. + */ + public function between($val, $x, $y) + { + return $val . ' BETWEEN ' . $x . ' AND ' . $y; + } + + /** + * Creates an instance of TRIM() function, with the given argument. + * + * @param mixed $x Argument to be used as argument of TRIM() function. + * + * @return Expr\Func a TRIM expression. + */ + public function trim($x) + { + return new Expr\Func('TRIM', $x); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php new file mode 100644 index 00000000..432f714f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Andx extends Composite +{ + /** + * @var string + */ + protected $separator = ' AND '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Orx', + 'Doctrine\ORM\Query\Expr\Andx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php new file mode 100644 index 00000000..6eac70a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Abstract base Expr class for building DQL parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Base +{ + /** + * @var string + */ + protected $preSeparator = '('; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ')'; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param array $args + */ + public function __construct($args = array()) + { + $this->addMultiple($args); + } + + /** + * @param array $args + * + * @return Base + */ + public function addMultiple($args = array()) + { + foreach ((array) $args as $arg) { + $this->add($arg); + } + + return $this; + } + + /** + * @param mixed $arg + * + * @return Base + * + * @throws \InvalidArgumentException + */ + public function add($arg) + { + if ( $arg !== null && (!$arg instanceof self || $arg->count() > 0) ) { + // If we decide to keep Expr\Base instances, we can use this check + if ( ! is_string($arg)) { + $class = get_class($arg); + + if ( ! in_array($class, $this->allowedClasses)) { + throw new \InvalidArgumentException("Expression of type '$class' not allowed in this context."); + } + } + + $this->parts[] = $arg; + } + + return $this; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return string + */ + public function __toString() + { + if ($this->count() == 1) { + return (string) $this->parts[0]; + } + + return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php new file mode 100644 index 00000000..4103dcea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL comparison expressions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Comparison +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a comparison expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + public function __toString() + { + return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php new file mode 100644 index 00000000..4d9a2519 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Composite extends Base +{ + /** + * @return string + */ + public function __toString() + { + if ($this->count() === 1) { + return (string) $this->parts[0]; + } + + $components = array(); + + foreach ($this->parts as $part) { + $components[] = $this->processQueryPart($part); + } + + return implode($this->separator, $components); + } + + /** + * @param string $part + * + * @return string + */ + private function processQueryPart($part) + { + $queryPart = (string) $part; + + if (is_object($part) && $part instanceof self && $part->count() > 1) { + return $this->preSeparator . $queryPart . $this->postSeparator; + } + + // Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND") + if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) { + return $this->preSeparator . $queryPart . $this->postSeparator; + } + + return $queryPart; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php new file mode 100644 index 00000000..9dcce9bb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL from. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class From +{ + /** + * @var string + */ + protected $from; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $indexBy; + + /** + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + */ + public function __construct($from, $alias, $indexBy = null) + { + $this->from = $from; + $this->alias = $alias; + $this->indexBy = $indexBy; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return string + */ + public function getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return $this->from . ' ' . $this->alias . + ($this->indexBy ? ' INDEX BY ' . $this->indexBy : ''); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php new file mode 100644 index 00000000..b4ed07cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Func +{ + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $arguments; + + /** + * Creates a function, with the given argument. + * + * @param string $name + * @param array $arguments + */ + public function __construct($name, $arguments) + { + $this->name = $name; + $this->arguments = (array) $arguments; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * @return string + */ + public function __toString() + { + return $this->name . '(' . implode(', ', $this->arguments) . ')'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php new file mode 100644 index 00000000..efa3582b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Group By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupBy extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php new file mode 100644 index 00000000..7a59e247 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL join. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join +{ + const INNER_JOIN = 'INNER'; + const LEFT_JOIN = 'LEFT'; + + const ON = 'ON'; + const WITH = 'WITH'; + + /** + * @var string + */ + protected $joinType; + + /** + * @var string + */ + protected $join; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $conditionType; + + /** + * @var string + */ + protected $condition; + + /** + * @var string + */ + protected $indexBy; + + /** + * @param string $joinType The condition type constant. Either INNER_JOIN or LEFT_JOIN. + * @param string $join The relationship to join. + * @param string|null $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + */ + public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null) + { + $this->joinType = $joinType; + $this->join = $join; + $this->alias = $alias; + $this->conditionType = $conditionType; + $this->condition = $condition; + $this->indexBy = $indexBy; + } + + /** + * @return string + */ + public function getJoinType() + { + return $this->joinType; + } + + /** + * @return string + */ + public function getJoin() + { + return $this->join; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return string + */ + public function getConditionType() + { + return $this->conditionType; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return string + */ + public function getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return strtoupper($this->joinType) . ' JOIN ' . $this->join + . ($this->alias ? ' ' . $this->alias : '') + . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '') + . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : ''); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php new file mode 100644 index 00000000..98cee79d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Literal extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php new file mode 100644 index 00000000..9bf800de --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL math statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Math +{ + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a mathematical expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + public function __toString() + { + // Adjusting Left Expression + $leftExpr = (string) $this->leftExpr; + + if ($this->leftExpr instanceof Math) { + $leftExpr = '(' . $leftExpr . ')'; + } + + // Adjusting Right Expression + $rightExpr = (string) $this->rightExpr; + + if ($this->rightExpr instanceof Math) { + $rightExpr = '(' . $rightExpr . ')'; + } + + return $leftExpr . ' ' . $this->operator . ' ' . $rightExpr; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php new file mode 100644 index 00000000..932548bd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Order By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderBy +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param string|null $sort + * @param string|null $order + */ + public function __construct($sort = null, $order = null) + { + if ($sort) { + $this->add($sort, $order); + } + } + + /** + * @param string $sort + * @param string|null $order + * + * @return void + */ + public function add($sort, $order = null) + { + $order = ! $order ? 'ASC' : $order; + $this->parts[] = $sort . ' '. $order; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } + + /** + * @return string + */ + public function __tostring() + { + return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php new file mode 100644 index 00000000..c4ff7ae3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL OR clauses. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Orx extends Composite +{ + /** + * @var string + */ + protected $separator = ' OR '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Andx', + 'Doctrine\ORM\Query\Expr\Orx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php new file mode 100644 index 00000000..21df6c73 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL select statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Select extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Func' + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php new file mode 100644 index 00000000..fc749f15 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\ORM\Query\Filter; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ParameterTypeInferer; + +/** + * The base class that user defined filters should extend. + * + * Handles the setting and escaping of parameters. + * + * @author Alexander + * @author Benjamin Eberlei + * @abstract + */ +abstract class SQLFilter +{ + /** + * The entity manager. + * + * @var EntityManager + */ + private $em; + + /** + * Parameters for the filter. + * + * @var array + */ + private $parameters; + + /** + * Constructs the SQLFilter object. + * + * @param EntityManager $em The entity manager. + */ + final public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Sets a parameter that can be used by the filter. + * + * @param string $name Name of the parameter. + * @param string $value Value of the parameter. + * @param string|null $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * + * @return SQLFilter The current SQL filter. + */ + final public function setParameter($name, $value, $type = null) + { + if (null === $type) { + $type = ParameterTypeInferer::inferType($value); + } + + $this->parameters[$name] = array('value' => $value, 'type' => $type); + + // Keep the parameters sorted for the hash + ksort($this->parameters); + + // The filter collection of the EM is now dirty + $this->em->getFilters()->setFiltersStateDirty(); + + return $this; + } + + /** + * Gets a parameter to use in a query. + * + * The function is responsible for the right output escaping to use the + * value in a query. + * + * @param string $name Name of the parameter. + * + * @return string The SQL escaped parameter to use in a query. + * + * @throws \InvalidArgumentException + */ + final public function getParameter($name) + { + if (!isset($this->parameters[$name])) { + throw new \InvalidArgumentException("Parameter '" . $name . "' does not exist."); + } + + return $this->em->getConnection()->quote($this->parameters[$name]['value'], $this->parameters[$name]['type']); + } + + /** + * Returns as string representation of the SQLFilter parameters (the state). + * + * @return string String representation of the SQLFilter. + */ + final public function __toString() + { + return serialize($this->parameters); + } + + /** + * Gets the SQL query part to add to a query. + * + * @param ClassMetaData $targetEntity + * @param string $targetTableAlias + * + * @return string The constraint SQL if there is available, empty string otherwise. + */ + abstract public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php new file mode 100644 index 00000000..e26d093f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php @@ -0,0 +1,211 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Configuration; +use Doctrine\ORM\EntityManager; + +/** + * Collection class for all the query filters. + * + * @author Alexander + */ +class FilterCollection +{ + /* Filter STATES */ + + /** + * A filter object is in CLEAN state when it has no changed parameters. + */ + const FILTERS_STATE_CLEAN = 1; + + /** + * A filter object is in DIRTY state when it has changed parameters. + */ + const FILTERS_STATE_DIRTY = 2; + + /** + * The used Configuration. + * + * @var \Doctrine\ORM\Configuration + */ + private $config; + + /** + * The EntityManager that "owns" this FilterCollection instance. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Instances of enabled filters. + * + * @var array + */ + private $enabledFilters = array(); + + /** + * @var string The filter hash from the last time the query was parsed. + */ + private $filterHash; + + /** + * @var integer The current state of this filter. + */ + private $filtersState = self::FILTERS_STATE_CLEAN; + + /** + * Constructor. + * + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->config = $em->getConfiguration(); + } + + /** + * Gets all the enabled filters. + * + * @return array The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The enabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function enable($name) + { + if (null === $filterClass = $this->config->getFilterClassName($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if (!isset($this->enabledFilters[$name])) { + $this->enabledFilters[$name] = new $filterClass($this->em); + + // Keep the enabled filters sorted for the hash + ksort($this->enabledFilters); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + } + + return $this->enabledFilters[$name]; + } + + /** + * Disables a filter. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The disabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function disable($name) + { + // Get the filter to return it + $filter = $this->getFilter($name); + + unset($this->enabledFilters[$name]); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + + return $filter; + } + + /** + * Gets an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if (!isset($this->enabledFilters[$name])) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + + return $this->enabledFilters[$name]; + } + + /** + * Checks if a filter is enabled. + * + * @param string $name Name of the filter. + * + * @return boolean True if the filter is enabled, false otherwise. + */ + public function isEnabled($name) + { + return isset($this->enabledFilters[$name]); + } + + /** + * @return boolean True, if the filter collection is clean. + */ + public function isClean() + { + return self::FILTERS_STATE_CLEAN === $this->filtersState; + } + + /** + * Generates a string of currently enabled filters to use for the cache id. + * + * @return string + */ + public function getHash() + { + // If there are only clean filters, the previous hash can be returned + if (self::FILTERS_STATE_CLEAN === $this->filtersState) { + return $this->filterHash; + } + + $filterHash = ''; + foreach ($this->enabledFilters as $name => $filter) { + $filterHash .= $name . $filter; + } + + return $filterHash; + } + + /** + * Sets the filter state to dirty. + */ + public function setFiltersStateDirty() + { + $this->filtersState = self::FILTERS_STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php new file mode 100644 index 00000000..bf0b5f6d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php @@ -0,0 +1,207 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Scans a DQL query for tokens. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @since 2.0 + */ +class Lexer extends \Doctrine\Common\Lexer +{ + // All tokens that are not valid identifiers must be < 100 + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_INPUT_PARAMETER = 4; + const T_FLOAT = 5; + const T_CLOSE_PARENTHESIS = 6; + const T_OPEN_PARENTHESIS = 7; + const T_COMMA = 8; + const T_DIVIDE = 9; + const T_DOT = 10; + const T_EQUALS = 11; + const T_GREATER_THAN = 12; + const T_LOWER_THAN = 13; + const T_MINUS = 14; + const T_MULTIPLY = 15; + const T_NEGATE = 16; + const T_PLUS = 17; + const T_OPEN_CURLY_BRACE = 18; + const T_CLOSE_CURLY_BRACE = 19; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + const T_ALL = 101; + const T_AND = 102; + const T_ANY = 103; + const T_AS = 104; + const T_ASC = 105; + const T_AVG = 106; + const T_BETWEEN = 107; + const T_BOTH = 108; + const T_BY = 109; + const T_CASE = 110; + const T_COALESCE = 111; + const T_COUNT = 112; + const T_DELETE = 113; + const T_DESC = 114; + const T_DISTINCT = 115; + const T_ELSE = 116; + const T_EMPTY = 117; + const T_END = 118; + const T_ESCAPE = 119; + const T_EXISTS = 120; + const T_FALSE = 121; + const T_FROM = 122; + const T_GROUP = 123; + const T_HAVING = 124; + const T_HIDDEN = 125; + const T_IN = 126; + const T_INDEX = 127; + const T_INNER = 128; + const T_INSTANCE = 129; + const T_IS = 130; + const T_JOIN = 131; + const T_LEADING = 132; + const T_LEFT = 133; + const T_LIKE = 134; + const T_MAX = 135; + const T_MEMBER = 136; + const T_MIN = 137; + const T_NOT = 138; + const T_NULL = 139; + const T_NULLIF = 140; + const T_OF = 141; + const T_OR = 142; + const T_ORDER = 143; + const T_OUTER = 144; + const T_SELECT = 145; + const T_SET = 146; + const T_SOME = 147; + const T_SUM = 148; + const T_THEN = 149; + const T_TRAILING = 150; + const T_TRUE = 151; + const T_UPDATE = 152; + const T_WHEN = 153; + const T_WHERE = 154; + const T_WITH = 155; + const T_PARTIAL = 156; + const T_NEW = 157; + + /** + * Creates a new query scanner object. + * + * @param string $input A query string. + */ + public function __construct($input) + { + $this->setInput($input); + } + + /** + * @inheritdoc + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}', + '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', + "'(?:[^']|'')*'", + '\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}' + ); + } + + /** + * @inheritdoc + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '(.)'); + } + + /** + * @inheritdoc + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + switch (true) { + // Recognize numeric values + case (is_numeric($value)): + if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { + return self::T_FLOAT; + } + + return self::T_INTEGER; + + // Recognize quoted strings + case ($value[0] === "'"): + $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + + // Recognize identifiers + case (ctype_alpha($value[0]) || $value[0] === '_'): + $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); + + if (defined($name)) { + $type = constant($name); + + if ($type > 100) { + return $type; + } + } + + return self::T_IDENTIFIER; + + // Recognize input parameters + case ($value[0] === '?' || $value[0] === ':'): + return self::T_INPUT_PARAMETER; + + // Recognize symbols + case ($value === '.'): return self::T_DOT; + case ($value === ','): return self::T_COMMA; + case ($value === '('): return self::T_OPEN_PARENTHESIS; + case ($value === ')'): return self::T_CLOSE_PARENTHESIS; + case ($value === '='): return self::T_EQUALS; + case ($value === '>'): return self::T_GREATER_THAN; + case ($value === '<'): return self::T_LOWER_THAN; + case ($value === '+'): return self::T_PLUS; + case ($value === '-'): return self::T_MINUS; + case ($value === '*'): return self::T_MULTIPLY; + case ($value === '/'): return self::T_DIVIDE; + case ($value === '!'): return self::T_NEGATE; + case ($value === '{'): return self::T_OPEN_CURLY_BRACE; + case ($value === '}'): return self::T_CLOSE_CURLY_BRACE; + + // Default + default: + // Do nothing + } + + return $type; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php new file mode 100644 index 00000000..39e2a7a4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Defines a Query Parameter. + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class Parameter +{ + /** + * The parameter name. + * + * @var string + */ + private $name; + + /** + * The parameter value. + * + * @var mixed + */ + private $value; + + /** + * The parameter type. + * + * @var mixed + */ + private $type; + + /** + * Constructor. + * + * @param string $name Parameter name + * @param mixed $value Parameter value + * @param mixed $type Parameter type + */ + public function __construct($name, $value, $type = null) + { + $this->name = trim($name, ':'); + + $this->setValue($value, $type); + } + + /** + * Retrieves the Parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieves the Parameter value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Retrieves the Parameter type. + * + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * Defines the Parameter value. + * + * @param mixed $value Parameter value. + * @param mixed $type Parameter type. + */ + public function setValue($value, $type = null) + { + $this->value = $value; + $this->type = $type ?: ParameterTypeInferer::inferType($value); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php new file mode 100644 index 00000000..462f971b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Provides an enclosed support for parameter inferring. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ParameterTypeInferer +{ + /** + * Infers type of a given value, returning a compatible constant: + * - Type (\Doctrine\DBAL\Types\Type::*) + * - Connection (\Doctrine\DBAL\Connection::PARAM_*) + * + * @param mixed $value Parameter value. + * + * @return mixed Parameter type constant. + */ + public static function inferType($value) + { + if (is_integer($value)) { + return Type::INTEGER; + } + + if (is_bool($value)) { + return Type::BOOLEAN; + } + + if ($value instanceof \DateTime) { + return Type::DATETIME; + } + + if (is_array($value)) { + return is_integer(current($value)) + ? Connection::PARAM_INT_ARRAY + : Connection::PARAM_STR_ARRAY; + } + + return \PDO::PARAM_STR; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php new file mode 100644 index 00000000..67ee0729 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php @@ -0,0 +1,3381 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. + * Parses a DQL query, reports any errors in it, and generates an AST. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Janne Vanhala + * @author Fabio B. Silva + */ +class Parser +{ + /** + * READ-ONLY: Maps BUILT-IN string function names to AST class names. + * + * @var array + */ + private static $_STRING_FUNCTIONS = array( + 'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction', + 'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction', + 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', + 'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', + 'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction', + 'identity' => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction', + ); + + /** + * READ-ONLY: Maps BUILT-IN numeric function names to AST class names. + * + * @var array + */ + private static $_NUMERIC_FUNCTIONS = array( + 'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction', + 'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction', + 'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction', + 'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction', + 'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction', + 'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction', + 'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction', + 'bit_and' => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction', + 'bit_or' => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction', + ); + + /** + * READ-ONLY: Maps BUILT-IN datetime function names to AST class names. + * + * @var array + */ + private static $_DATETIME_FUNCTIONS = array( + 'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction', + 'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction', + 'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction', + 'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction', + 'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction', + ); + + /* + * Expressions that were encountered during parsing of identifiers and expressions + * and still need to be validated. + */ + + /** + * @var array + */ + private $deferredIdentificationVariables = array(); + + /** + * @var array + */ + private $deferredPartialObjectExpressions = array(); + + /** + * @var array + */ + private $deferredPathExpressions = array(); + + /** + * @var array + */ + private $deferredResultVariables = array(); + + /** + * @var array + */ + private $deferredNewObjectExpressions = array(); + + /** + * The lexer. + * + * @var \Doctrine\ORM\Query\Lexer + */ + private $lexer; + + /** + * The parser result. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $parserResult; + + /** + * The EntityManager. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The Query to parse. + * + * @var Query + */ + private $query; + + /** + * Map of declared query components in the parsed query. + * + * @var array + */ + private $queryComponents = array(); + + /** + * Keeps the nesting level of defined ResultVariables. + * + * @var integer + */ + private $nestingLevel = 0; + + /** + * Any additional custom tree walkers that modify the AST. + * + * @var array + */ + private $customTreeWalkers = array(); + + /** + * The custom last tree walker, if any, that is responsible for producing the output. + * + * @var TreeWalker + */ + private $customOutputWalker; + + /** + * @var array + */ + private $identVariableExpressions = array(); + + /** + * Checks if a function is internally defined. Used to prevent overwriting + * of built-in functions through user-defined functions. + * + * @param string $functionName + * + * @return bool + */ + static public function isInternalFunction($functionName) + { + $functionName = strtolower($functionName); + + return isset(self::$_STRING_FUNCTIONS[$functionName]) + || isset(self::$_DATETIME_FUNCTIONS[$functionName]) + || isset(self::$_NUMERIC_FUNCTIONS[$functionName]); + } + + /** + * Creates a new query parser object. + * + * @param Query $query The Query to parse. + */ + public function __construct(Query $query) + { + $this->query = $query; + $this->em = $query->getEntityManager(); + $this->lexer = new Lexer($query->getDql()); + $this->parserResult = new ParserResult(); + } + + /** + * Sets a custom tree walker that produces output. + * This tree walker will be run last over the AST, after any other walkers. + * + * @param string $className + * + * @return void + */ + public function setCustomOutputTreeWalker($className) + { + $this->customOutputWalker = $className; + } + + /** + * Adds a custom tree walker for modifying the AST. + * + * @param string $className + * + * @return void + */ + public function addCustomTreeWalker($className) + { + $this->customTreeWalkers[] = $className; + } + + /** + * Gets the lexer used by the parser. + * + * @return \Doctrine\ORM\Query\Lexer + */ + public function getLexer() + { + return $this->lexer; + } + + /** + * Gets the ParserResult that is being filled with information during parsing. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + public function getParserResult() + { + return $this->parserResult; + } + + /** + * Gets the EntityManager used by the parser. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Parses and builds AST for the given Query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + // Parse & build AST + $AST = $this->QueryLanguage(); + + // Process any deferred validations of some nodes in the AST. + // This also allows post-processing of the AST for modification purposes. + $this->processDeferredIdentificationVariables(); + + if ($this->deferredPartialObjectExpressions) { + $this->processDeferredPartialObjectExpressions(); + } + + if ($this->deferredPathExpressions) { + $this->processDeferredPathExpressions($AST); + } + + if ($this->deferredResultVariables) { + $this->processDeferredResultVariables(); + } + + if ($this->deferredNewObjectExpressions) { + $this->processDeferredNewObjectExpressions($AST); + } + + $this->processRootEntityAliasSelected(); + + // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot! + $this->fixIdentificationVariableOrder($AST); + + return $AST; + } + + /** + * Attempts to match the given token with the current lookahead token. + * + * If they match, updates the lookahead token; otherwise raises a syntax + * error. + * + * @param int $token The token type. + * + * @return void + * + * @throws QueryException If the tokens don't match. + */ + public function match($token) + { + $lookaheadType = $this->lexer->lookahead['type']; + + // short-circuit on first condition, usually types match + if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) { + $this->syntaxError($this->lexer->getLiteral($token)); + } + + $this->lexer->moveNext(); + } + + /** + * Frees this parser, enabling it to be reused. + * + * @param boolean $deep Whether to clean peek and reset errors. + * @param integer $position Position to reset. + * + * @return void + */ + public function free($deep = false, $position = 0) + { + // WARNING! Use this method with care. It resets the scanner! + $this->lexer->resetPosition($position); + + // Deep = true cleans peek and also any previously defined errors + if ($deep) { + $this->lexer->resetPeek(); + } + + $this->lexer->token = null; + $this->lexer->lookahead = null; + } + + /** + * Parses a query string. + * + * @return ParserResult + */ + public function parse() + { + $AST = $this->getAST(); + + if (($customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { + $this->customTreeWalkers = $customWalkers; + } + + if (($customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) { + $this->customOutputWalker = $customOutputWalker; + } + + // Run any custom tree walkers over the AST + if ($this->customTreeWalkers) { + $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents); + + foreach ($this->customTreeWalkers as $walker) { + $treeWalkerChain->addTreeWalker($walker); + } + + switch (true) { + case ($AST instanceof AST\UpdateStatement): + $treeWalkerChain->walkUpdateStatement($AST); + break; + + case ($AST instanceof AST\DeleteStatement): + $treeWalkerChain->walkDeleteStatement($AST); + break; + + case ($AST instanceof AST\SelectStatement): + default: + $treeWalkerChain->walkSelectStatement($AST); + } + + $this->queryComponents = $treeWalkerChain->getQueryComponents(); + } + + $outputWalkerClass = $this->customOutputWalker ?: __NAMESPACE__ . '\SqlWalker'; + $outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents); + + // Assign an SQL executor to the parser result + $this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST)); + + return $this->parserResult; + } + + /** + * Fixes order of identification variables. + * + * They have to appear in the select clause in the same order as the + * declarations (from ... x join ... y join ... z ...) appear in the query + * as the hydration process relies on that order for proper operation. + * + * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST + * + * @return void + */ + private function fixIdentificationVariableOrder($AST) + { + if (count($this->identVariableExpressions) <= 1) { + return; + } + + foreach ($this->queryComponents as $dqlAlias => $qComp) { + if ( ! isset($this->identVariableExpressions[$dqlAlias])) { + continue; + } + + $expr = $this->identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + + unset($AST->selectClause->selectExpressions[$key]); + + $AST->selectClause->selectExpressions[] = $expr; + } + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array|null $token Got token. + * + * @return void + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function syntaxError($expected = '', $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $tokenPos = (isset($token['position'])) ? $token['position'] : '-1'; + + $message = "line 0, col {$tokenPos}: Error: "; + $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected '; + $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'"; + + throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Generates a new semantical error. + * + * @param string $message Optional message. + * @param array|null $token Optional token. + * + * @return void + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function semanticalError($message = '', $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + // Minimum exposed chars ahead of token + $distance = 12; + + // Find a position of a final word to display in error string + $dql = $this->query->getDql(); + $length = strlen($dql); + $pos = $token['position'] + $distance; + $pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length); + $length = ($pos !== false) ? $pos - $token['position'] : $distance; + + $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1'; + $tokenStr = substr($dql, $token['position'], $length); + + // Building informative message + $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message; + + throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Peeks beyond the matched closing parenthesis and returns the first token after that one. + * + * @param boolean $resetPeek Reset peek after finding the closing parenthesis. + * + * @return array + */ + private function peekBeyondClosingParenthesis($resetPeek = true) + { + $token = $this->lexer->peek(); + $numUnmatched = 1; + + while ($numUnmatched > 0 && $token !== null) { + switch ($token['type']) { + case Lexer::T_OPEN_PARENTHESIS: + ++$numUnmatched; + break; + + case Lexer::T_CLOSE_PARENTHESIS: + --$numUnmatched; + break; + + default: + // Do nothing + } + + $token = $this->lexer->peek(); + } + + if ($resetPeek) { + $this->lexer->resetPeek(); + } + + return $token; + } + + /** + * Checks if the given token indicates a mathematical operator. + * + * @param array $token + * + * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise. + */ + private function isMathOperator($token) + { + return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY)); + } + + /** + * Checks if the next-next (after lookahead) token starts a function. + * + * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise. + */ + private function isFunction() + { + $lookaheadType = $this->lexer->lookahead['type']; + $peek = $this->lexer->peek(); + + $this->lexer->resetPeek(); + + return ($lookaheadType >= Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_OPEN_PARENTHESIS); + } + + /** + * Checks whether the given token type indicates an aggregate function. + * + * @param int $tokenType + * + * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise. + */ + private function isAggregateFunction($tokenType) + { + return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT)); + } + + /** + * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME. + * + * @return boolean + */ + private function isNextAllAnySome() + { + return in_array($this->lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME)); + } + + /** + * Validates that the given IdentificationVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredIdentificationVariables() + { + foreach ($this->deferredIdentificationVariables as $deferredItem) { + $identVariable = $deferredItem['expression']; + + // Check if IdentificationVariable exists in queryComponents + if ( ! isset($this->queryComponents[$identVariable])) { + $this->semanticalError( + "'$identVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->queryComponents[$identVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['metadata'])) { + $this->semanticalError( + "'$identVariable' does not point to a Class.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given NewObjectExpression. + * + * @param \Doctrine\ORM\Query\AST\SelectClause $AST + * + * @return void + */ + private function processDeferredNewObjectExpressions($AST) + { + foreach ($this->deferredNewObjectExpressions as $deferredItem) { + $expression = $deferredItem['expression']; + $token = $deferredItem['token']; + $className = $expression->className; + $args = $expression->args; + $fromClassName = isset($AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName) + ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName + : null; + + // If the namespace is not given then assumes the first FROM entity namespace + if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) { + $namespace = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); + $fqcn = $namespace . '\\' . $className; + + if (class_exists($fqcn)) { + $expression->className = $fqcn; + $className = $fqcn; + } + } + + if ( ! class_exists($className)) { + $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token); + } + + $class = new \ReflectionClass($className); + + if ( ! $class->isInstantiable()) { + $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token); + } + + if ($class->getConstructor() === null) { + $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token); + } + + if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) { + $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token); + } + } + } + + /** + * Validates that the given PartialObjectExpression is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredPartialObjectExpressions() + { + foreach ($this->deferredPartialObjectExpressions as $deferredItem) { + $expr = $deferredItem['expression']; + $class = $this->queryComponents[$expr->identificationVariable]['metadata']; + + foreach ($expr->partialFieldSet as $field) { + if (isset($class->fieldMappings[$field])) { + continue; + } + + $this->semanticalError( + "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token'] + ); + } + + if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) { + $this->semanticalError( + "The partial field selection of class " . $class->name . " must contain the identifier.", + $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given ResultVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredResultVariables() + { + foreach ($this->deferredResultVariables as $deferredItem) { + $resultVariable = $deferredItem['expression']; + + // Check if ResultVariable exists in queryComponents + if ( ! isset($this->queryComponents[$resultVariable])) { + $this->semanticalError( + "'$resultVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->queryComponents[$resultVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['resultVariable'])) { + $this->semanticalError( + "'$resultVariable' does not point to a ResultVariable.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given PathExpression is semantically correct for grammar rules: + * + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @param mixed $AST + * + * @return void + */ + private function processDeferredPathExpressions($AST) + { + foreach ($this->deferredPathExpressions as $deferredItem) { + $pathExpression = $deferredItem['expression']; + + $qComp = $this->queryComponents[$pathExpression->identificationVariable]; + $class = $qComp['metadata']; + + if (($field = $pathExpression->field) === null) { + $field = $pathExpression->field = $class->identifier[0]; + } + + // Check if field or association exists + if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) { + $this->semanticalError( + 'Class ' . $class->name . ' has no field or association named ' . $field, + $deferredItem['token'] + ); + } + + $fieldType = AST\PathExpression::TYPE_STATE_FIELD; + + if (isset($class->associationMappings[$field])) { + $assoc = $class->associationMappings[$field]; + + $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE) + ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION; + } + + // Validate if PathExpression is one of the expected types + $expectedType = $pathExpression->expectedType; + + if ( ! ($expectedType & $fieldType)) { + // We need to recognize which was expected type(s) + $expectedStringTypes = array(); + + // Validate state field type + if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) { + $expectedStringTypes[] = 'StateFieldPathExpression'; + } + + // Validate single valued association (*-to-one) + if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'SingleValuedAssociationField'; + } + + // Validate single valued association (*-to-many) + if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'CollectionValuedAssociationField'; + } + + // Build the error message + $semanticalError = 'Invalid PathExpression. '; + $semanticalError .= (count($expectedStringTypes) == 1) + ? 'Must be a ' . $expectedStringTypes[0] . '.' + : implode(' or ', $expectedStringTypes) . ' expected.'; + + $this->semanticalError($semanticalError, $deferredItem['token']); + } + + // We need to force the type in PathExpression + $pathExpression->type = $fieldType; + } + } + + /** + * @return void + */ + private function processRootEntityAliasSelected() + { + if ( ! count($this->identVariableExpressions)) { + return; + } + + $foundRootEntity = false; + + foreach ($this->identVariableExpressions as $dqlAlias => $expr) { + if (isset($this->queryComponents[$dqlAlias]) && $this->queryComponents[$dqlAlias]['parent'] === null) { + $foundRootEntity = true; + } + } + + if ( ! $foundRootEntity) { + $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.'); + } + } + + /** + * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function QueryLanguage() + { + $this->lexer->moveNext(); + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_SELECT: + $statement = $this->SelectStatement(); + break; + + case Lexer::T_UPDATE: + $statement = $this->UpdateStatement(); + break; + + case Lexer::T_DELETE: + $statement = $this->DeleteStatement(); + break; + + default: + $this->syntaxError('SELECT, UPDATE or DELETE'); + break; + } + + // Check for end of string + if ($this->lexer->lookahead !== null) { + $this->syntaxError('end of string'); + } + + return $statement; + } + + /** + * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\SelectStatement + */ + public function SelectStatement() + { + $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause()); + + $selectStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $selectStatement->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $selectStatement->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $selectStatement->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + return $selectStatement; + } + + /** + * UpdateStatement ::= UpdateClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\UpdateStatement + */ + public function UpdateStatement() + { + $updateStatement = new AST\UpdateStatement($this->UpdateClause()); + + $updateStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $updateStatement; + } + + /** + * DeleteStatement ::= DeleteClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function DeleteStatement() + { + $deleteStatement = new AST\DeleteStatement($this->DeleteClause()); + + $deleteStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $deleteStatement; + } + + /** + * IdentificationVariable ::= identifier + * + * @return string + */ + public function IdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $identVariable = $this->lexer->token['value']; + + $this->deferredIdentificationVariables[] = array( + 'expression' => $identVariable, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $identVariable; + } + + /** + * AliasIdentificationVariable = identifier + * + * @return string + */ + public function AliasIdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $aliasIdentVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$aliasIdentVariable]); + + if ($exists) { + $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->lexer->token); + } + + return $aliasIdentVariable; + } + + /** + * AbstractSchemaName ::= identifier + * + * @return string + */ + public function AbstractSchemaName() + { + $this->match(Lexer::T_IDENTIFIER); + + $schemaName = ltrim($this->lexer->token['value'], '\\'); + + if (strrpos($schemaName, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $schemaName); + + $schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $exists = class_exists($schemaName, true); + + if ( ! $exists) { + $this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token); + } + + return $schemaName; + } + + /** + * AliasResultVariable ::= identifier + * + * @return string + */ + public function AliasResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$resultVariable]); + + if ($exists) { + $this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token); + } + + return $resultVariable; + } + + /** + * ResultVariable ::= identifier + * + * @return string + */ + public function ResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->lexer->token['value']; + + // Defer ResultVariable validation + $this->deferredResultVariables[] = array( + 'expression' => $resultVariable, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $resultVariable; + } + + /** + * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationPathExpression() + { + $identVariable = $this->IdentificationVariable(); + + if ( ! isset($this->queryComponents[$identVariable])) { + $this->semanticalError( + 'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.' + ); + } + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->lexer->token['value']; + + // Validate association field + $qComp = $this->queryComponents[$identVariable]; + $class = $qComp['metadata']; + + if ( ! $class->hasAssociation($field)) { + $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field); + } + + return new AST\JoinAssociationPathExpression($identVariable, $field); + } + + /** + * Parses an arbitrary path expression and defers semantical validation + * based on expected types. + * + * PathExpression ::= IdentificationVariable "." identifier + * + * @param integer $expectedTypes + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function PathExpression($expectedTypes) + { + $identVariable = $this->IdentificationVariable(); + $field = null; + + if ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->lexer->token['value']; + } + + // Creating AST node + $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); + + // Defer PathExpression validation if requested to be deferred + $this->deferredPathExpressions[] = array( + 'expression' => $pathExpr, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $pathExpr; + } + + /** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function AssociationPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION | + AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION + ); + } + + /** + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_STATE_FIELD | + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + ); + } + + /** + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function StateFieldPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD); + } + + /** + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedAssociationPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION); + } + + /** + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function CollectionValuedPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION); + } + + /** + * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @return \Doctrine\ORM\Query\AST\SelectClause + */ + public function SelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + // Check for DISTINCT + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + // Process SelectExpressions (1..N) + $selectExpressions = array(); + $selectExpressions[] = $this->SelectExpression(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $selectExpressions[] = $this->SelectExpression(); + } + + return new AST\SelectClause($selectExpressions, $isDistinct); + } + + /** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectClause + */ + public function SimpleSelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct); + } + + /** + * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + * + * @return \Doctrine\ORM\Query\AST\UpdateClause + */ + public function UpdateClause() + { + $this->match(Lexer::T_UPDATE); + $token = $this->lexer->lookahead; + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $class = $this->em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + $this->match(Lexer::T_SET); + + $updateItems = array(); + $updateItems[] = $this->UpdateItem(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $updateItems[] = $this->UpdateItem(); + } + + $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems); + $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable; + + return $updateClause; + } + + /** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\DeleteClause + */ + public function DeleteClause() + { + $this->match(Lexer::T_DELETE); + + if ($this->lexer->isNextToken(Lexer::T_FROM)) { + $this->match(Lexer::T_FROM); + } + + $token = $this->lexer->lookahead; + $deleteClause = new AST\DeleteClause($this->AbstractSchemaName()); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return $deleteClause; + } + + /** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\FromClause + */ + public function FromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariableDeclarations = array(); + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + } + + return new AST\FromClause($identificationVariableDeclarations); + } + + /** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\SubselectFromClause + */ + public function SubselectFromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariables = array(); + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + } + + return new AST\SubselectFromClause($identificationVariables); + } + + /** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\WhereClause + */ + public function WhereClause() + { + $this->match(Lexer::T_WHERE); + + return new AST\WhereClause($this->ConditionalExpression()); + } + + /** + * HavingClause ::= "HAVING" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\HavingClause + */ + public function HavingClause() + { + $this->match(Lexer::T_HAVING); + + return new AST\HavingClause($this->ConditionalExpression()); + } + + /** + * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + * + * @return \Doctrine\ORM\Query\AST\GroupByClause + */ + public function GroupByClause() + { + $this->match(Lexer::T_GROUP); + $this->match(Lexer::T_BY); + + $groupByItems = array($this->GroupByItem()); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $groupByItems[] = $this->GroupByItem(); + } + + return new AST\GroupByClause($groupByItems); + } + + /** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @return \Doctrine\ORM\Query\AST\OrderByClause + */ + public function OrderByClause() + { + $this->match(Lexer::T_ORDER); + $this->match(Lexer::T_BY); + + $orderByItems = array(); + $orderByItems[] = $this->OrderByItem(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $orderByItems[] = $this->OrderByItem(); + } + + return new AST\OrderByClause($orderByItems); + } + + /** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\Subselect + */ + public function Subselect() + { + // Increase query nesting level + $this->nestingLevel++; + + $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause()); + + $subselect->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $subselect->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $subselect->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $subselect->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + // Decrease query nesting level + $this->nestingLevel--; + + return $subselect; + } + + /** + * UpdateItem ::= SingleValuedPathExpression "=" NewValue + * + * @return \Doctrine\ORM\Query\AST\UpdateItem + */ + public function UpdateItem() + { + $pathExpr = $this->SingleValuedPathExpression(); + + $this->match(Lexer::T_EQUALS); + + $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue()); + + return $updateItem; + } + + /** + * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + * + * @return string | \Doctrine\ORM\Query\AST\PathExpression + */ + public function GroupByItem() + { + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + return $this->SingleValuedPathExpression(); + } + + // Still need to decide between IdentificationVariable or ResultVariable + $lookaheadValue = $this->lexer->lookahead['value']; + + if ( ! isset($this->queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot group by undefined identification or result variable.'); + } + + return (isset($this->queryComponents[$lookaheadValue]['metadata'])) + ? $this->IdentificationVariable() + : $this->ResultVariable(); + } + + /** + * OrderByItem ::= ( + * SimpleArithmeticExpression | SingleValuedPathExpression | + * ScalarExpression | ResultVariable + * ) ["ASC" | "DESC"] + * + * @return \Doctrine\ORM\Query\AST\OrderByItem + */ + public function OrderByItem() + { + + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + $this->lexer->resetPeek(); + $glimpse = $this->lexer->glimpse(); + + switch (true) { + + case ($this->isMathOperator($peek)): + $expr = $this->SimpleArithmeticExpression(); + + break; + case ($glimpse['type'] === Lexer::T_DOT): + $expr = $this->SingleValuedPathExpression(); + + break; + case ($this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis())): + $expr = $this->ScalarExpression(); + + break; + default: + $expr = $this->ResultVariable(); + + break; + } + + $type = 'ASC'; + $item = new AST\OrderByItem($expr); + + switch (true) { + case ($this->lexer->isNextToken(Lexer::T_DESC)): + $this->match(Lexer::T_DESC); + $type = 'DESC'; + break; + + case ($this->lexer->isNextToken(Lexer::T_ASC)): + $this->match(Lexer::T_ASC); + break; + + default: + // Do nothing + } + + $item->type = $type; + + return $item; + } + + /** + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * NOTE: Since it is not possible to correctly recognize individual types, here is the full + * grammar that needs to be supported: + * + * NewValue ::= SimpleArithmeticExpression | "NULL" + * + * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression + * + * @return AST\ArithmeticExpression + */ + public function NewValue() + { + if ($this->lexer->isNextToken(Lexer::T_NULL)) { + $this->match(Lexer::T_NULL); + + return null; + } + + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + return $this->ArithmeticExpression(); + } + + /** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + * + * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function IdentificationVariableDeclaration() + { + $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + $joins = array(); + + while ( + $this->lexer->isNextToken(Lexer::T_LEFT) || + $this->lexer->isNextToken(Lexer::T_INNER) || + $this->lexer->isNextToken(Lexer::T_JOIN) + ) { + $joins[] = $this->Join(); + } + + return new AST\IdentificationVariableDeclaration( + $rangeVariableDeclaration, $indexBy, $joins + ); + } + + /** + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + * + * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | + * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function SubselectIdentificationVariableDeclaration() + { + $this->lexer->glimpse(); + + /* NOT YET IMPLEMENTED! + + if ($glimpse['type'] == Lexer::T_DOT) { + $subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration(); + $subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression(); + $this->match(Lexer::T_AS); + $subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + return $subselectIdVarDecl; + } + */ + + return $this->IdentificationVariableDeclaration(); + } + + /** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" + * (JoinAssociationDeclaration | RangeVariableDeclaration) + * ["WITH" ConditionalExpression] + * + * @return \Doctrine\ORM\Query\AST\Join + */ + public function Join() + { + // Check Join type + $joinType = AST\Join::JOIN_TYPE_INNER; + + switch (true) { + case ($this->lexer->isNextToken(Lexer::T_LEFT)): + $this->match(Lexer::T_LEFT); + + $joinType = AST\Join::JOIN_TYPE_LEFT; + + // Possible LEFT OUTER join + if ($this->lexer->isNextToken(Lexer::T_OUTER)) { + $this->match(Lexer::T_OUTER); + + $joinType = AST\Join::JOIN_TYPE_LEFTOUTER; + } + break; + + case ($this->lexer->isNextToken(Lexer::T_INNER)): + $this->match(Lexer::T_INNER); + break; + + default: + // Do nothing + } + + $this->match(Lexer::T_JOIN); + + $next = $this->lexer->glimpse(); + $joinDeclaration = ($next['type'] === Lexer::T_DOT) + ? $this->JoinAssociationDeclaration() + : $this->RangeVariableDeclaration(); + + // Create AST node + $join = new AST\Join($joinType, $joinDeclaration); + + // Check for ad-hoc Join conditions + if ($this->lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) { + $this->match(Lexer::T_WITH); + + $join->conditionalExpression = $this->ConditionalExpression(); + } + + return $join; + } + + /** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration + */ + public function RangeVariableDeclaration() + { + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $token = $this->lexer->lookahead; + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $classMetadata = $this->em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $classMetadata, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable); + } + + /** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationDeclaration() + { + $joinAssociationPathExpression = $this->JoinAssociationPathExpression(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + + $identificationVariable = $joinAssociationPathExpression->identificationVariable; + $field = $joinAssociationPathExpression->associationField; + + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $targetClass, + 'parent' => $joinAssociationPathExpression->identificationVariable, + 'relation' => $class->getAssociationMapping($field), + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead + ); + + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy); + } + + /** + * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + * + * @return array + */ + public function PartialObjectExpression() + { + $this->match(Lexer::T_PARTIAL); + + $partialFieldSet = array(); + + $identificationVariable = $this->IdentificationVariable(); + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_OPEN_CURLY_BRACE); + $this->match(Lexer::T_IDENTIFIER); + + $partialFieldSet[] = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $this->match(Lexer::T_IDENTIFIER); + + $partialFieldSet[] = $this->lexer->token['value']; + } + + $this->match(Lexer::T_CLOSE_CURLY_BRACE); + + $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet); + + // Defer PartialObjectExpression validation + $this->deferredPartialObjectExpressions[] = array( + 'expression' => $partialObjectExpression, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $partialObjectExpression; + } + + /** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @return \Doctrine\ORM\Query\AST\NewObjectExpression + */ + public function NewObjectExpression() + { + $this->match(Lexer::T_NEW); + $this->match(Lexer::T_IDENTIFIER); + + $token = $this->lexer->token; + $className = $token['value']; + + if (strrpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + + $className = $this->em->getConfiguration() + ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $args[] = $this->NewObjectArg(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $args[] = $this->NewObjectArg(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $expression = new AST\NewObjectExpression($className, $args); + + // Defer NewObjectExpression validation + $this->deferredNewObjectExpressions[] = array( + 'token' => $token, + 'expression' => $expression, + 'nestingLevel' => $this->nestingLevel, + ); + + return $expression; + } + + /** + * NewObjectArg ::= ScalarExpression + * + * @TODO - Maybe you should support other expressions and nested "new" operator + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function NewObjectArg() + { + return $this->ScalarExpression(); + } + + /** + * IndexBy ::= "INDEX" "BY" StateFieldPathExpression + * + * @return \Doctrine\ORM\Query\AST\IndexBy + */ + public function IndexBy() + { + $this->match(Lexer::T_INDEX); + $this->match(Lexer::T_BY); + $pathExpr = $this->StateFieldPathExpression(); + + // Add the INDEX BY info to the query component + $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field; + + return new AST\IndexBy($pathExpr); + } + + /** + * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | + * StateFieldPathExpression | BooleanPrimary | CaseExpression | + * InstanceOfExpression + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function ScalarExpression() + { + $lookahead = $this->lexer->lookahead['type']; + $peek = $this->lexer->glimpse(); + + switch (true) { + case ($lookahead === Lexer::T_INTEGER): + case ($lookahead === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) or ( - 1 ) or ( + 1 ) + case ($lookahead === Lexer::T_MINUS): + case ($lookahead === Lexer::T_PLUS): + return $this->SimpleArithmeticExpression(); + + case ($lookahead === Lexer::T_STRING): + return $this->StringPrimary(); + + case ($lookahead === Lexer::T_TRUE): + case ($lookahead === Lexer::T_FALSE): + $this->match($lookahead); + + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); + + case ($lookahead === Lexer::T_INPUT_PARAMETER): + switch (true) { + case $this->isMathOperator($peek): + // :param + u.value + return $this->SimpleArithmeticExpression(); + + default: + return $this->InputParameter(); + } + + case ($lookahead === Lexer::T_CASE): + case ($lookahead === Lexer::T_COALESCE): + case ($lookahead === Lexer::T_NULLIF): + // Since NULLIF and COALESCE can be identified as a function, + // we need to check these before checking for FunctionDeclaration + return $this->CaseExpression(); + + case ($lookahead === Lexer::T_OPEN_PARENTHESIS): + return $this->SimpleArithmeticExpression(); + + //this check must be done before checking for a filed path expression + case ($this->isFunction()): + $this->lexer->peek(); // "(" + + switch (true) { + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + return $this->SimpleArithmeticExpression(); + + case ($this->isAggregateFunction($this->lexer->lookahead['type'])): + return $this->AggregateExpression(); + + default: + // IDENTITY(u) + return $this->FunctionDeclaration(); + } + + break; + //it is no function, so it must be a field path + case ($lookahead === Lexer::T_IDENTIFIER): + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + $this->lexer->resetPeek(); + + if ($this->isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + return $this->StateFieldPathExpression(); + + default: + $this->syntaxError(); + } + } + + /** + * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function CaseExpression() + { + $lookahead = $this->lexer->lookahead['type']; + + switch ($lookahead) { + case Lexer::T_NULLIF: + return $this->NullIfExpression(); + + case Lexer::T_COALESCE: + return $this->CoalesceExpression(); + + case Lexer::T_CASE: + $this->lexer->resetPeek(); + $peek = $this->lexer->peek(); + + if ($peek['type'] === Lexer::T_WHEN) { + return $this->GeneralCaseExpression(); + } + + return $this->SimpleCaseExpression(); + + default: + // Do nothing + break; + } + + $this->syntaxError(); + } + + /** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @return \Doctrine\ORM\Query\AST\CoalesceExpression + */ + public function CoalesceExpression() + { + $this->match(Lexer::T_COALESCE); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + // Process ScalarExpressions (1..N) + $scalarExpressions = array(); + $scalarExpressions[] = $this->ScalarExpression(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $scalarExpressions[] = $this->ScalarExpression(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\CoalesceExpression($scalarExpressions); + } + + /** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return \Doctrine\ORM\Query\AST\NullIfExpression + */ + public function NullIfExpression() + { + $this->match(Lexer::T_NULLIF); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $firstExpression = $this->ScalarExpression(); + $this->match(Lexer::T_COMMA); + $secondExpression = $this->ScalarExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\NullIfExpression($firstExpression, $secondExpression); + } + + /** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @return \Doctrine\ORM\Query\AST\GeneralCaseExpression + */ + public function GeneralCaseExpression() + { + $this->match(Lexer::T_CASE); + + // Process WhenClause (1..N) + $whenClauses = array(); + + do { + $whenClauses[] = $this->WhenClause(); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\GeneralCaseExpression($whenClauses, $scalarExpression); + } + + /** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + * + * @return AST\SimpleCaseExpression + */ + public function SimpleCaseExpression() + { + $this->match(Lexer::T_CASE); + $caseOperand = $this->StateFieldPathExpression(); + + // Process SimpleWhenClause (1..N) + $simpleWhenClauses = array(); + + do { + $simpleWhenClauses[] = $this->SimpleWhenClause(); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression); + } + + /** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\WhenClause + */ + public function WhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_THEN); + + return new AST\WhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleWhenClause + */ + public function SimpleWhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ScalarExpression(); + $this->match(Lexer::T_THEN); + + return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SelectExpression ::= ( + * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | + * PartialObjectExpression | "(" Subselect ")" | CaseExpression + * ) [["AS"] ["HIDDEN"] AliasResultVariable] + * + * @return \Doctrine\ORM\Query\AST\SelectExpression + */ + public function SelectExpression() + { + $expression = null; + $identVariable = null; + $peek = $this->lexer->glimpse(); + $lookaheadType = $this->lexer->lookahead['type']; + + switch (true) { + // ScalarExpression (u.name) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT): + $expression = $this->ScalarExpression(); + break; + + // IdentificationVariable (u) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $identVariable = $this->IdentificationVariable(); + break; + + // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) + case ($lookaheadType === Lexer::T_CASE): + case ($lookaheadType === Lexer::T_COALESCE): + case ($lookaheadType === Lexer::T_NULLIF): + $expression = $this->CaseExpression(); + break; + + // DQL Function (SUM(u.value) or SUM(u.value) + 1) + case ($this->isFunction()): + $this->lexer->peek(); // "(" + + switch (true) { + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + $expression = $this->ScalarExpression(); + break; + + case ($this->isAggregateFunction($lookaheadType)): + // COUNT(u.id) + $expression = $this->AggregateExpression(); + break; + + default: + // IDENTITY(u) + $expression = $this->FunctionDeclaration(); + break; + } + + break; + + // PartialObjectExpression (PARTIAL u.{id, name}) + case ($lookaheadType === Lexer::T_PARTIAL): + $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; + break; + + // Subselect + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT): + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + break; + + // Shortcut: ScalarExpression => SimpleArithmeticExpression + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS): + case ($lookaheadType === Lexer::T_INTEGER): + case ($lookaheadType === Lexer::T_STRING): + case ($lookaheadType === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) + case ($lookaheadType === Lexer::T_MINUS): + case ($lookaheadType === Lexer::T_PLUS): + $expression = $this->SimpleArithmeticExpression(); + break; + + // NewObjectExpression (New ClassName(id, name)) + case ($lookaheadType === Lexer::T_NEW): + $expression = $this->NewObjectExpression(); + break; + + default: + $this->syntaxError( + 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', + $this->lexer->lookahead + ); + } + + // [["AS"] ["HIDDEN"] AliasResultVariable] + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $hiddenAliasResultVariable = false; + + if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) { + $this->match(Lexer::T_HIDDEN); + + $hiddenAliasResultVariable = true; + } + + $aliasResultVariable = null; + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; + $aliasResultVariable = $this->AliasResultVariable(); + + // Include AliasResultVariable in query components. + $this->queryComponents[$aliasResultVariable] = array( + 'resultVariable' => $expression, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + } + + // AST + + $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable); + + if ($identVariable) { + $this->identVariableExpressions[$identVariable] = $expr; + } + + return $expr; + } + + /** + * SimpleSelectExpression ::= ( + * StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | + * AggregateExpression | "(" Subselect ")" | ScalarExpression + * ) [["AS"] AliasResultVariable] + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function SimpleSelectExpression() + { + $peek = $this->lexer->glimpse(); + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_IDENTIFIER: + switch (true) { + case ($peek['type'] === Lexer::T_DOT): + $expression = $this->StateFieldPathExpression(); + + return new AST\SimpleSelectExpression($expression); + + case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $this->IdentificationVariable(); + + return new AST\SimpleSelectExpression($expression); + + case ($this->isFunction()): + // SUM(u.id) + COUNT(u.id) + if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) { + return new AST\SimpleSelectExpression($this->ScalarExpression()); + } + // COUNT(u.id) + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { + return new AST\SimpleSelectExpression($this->AggregateExpression()); + } + // IDENTITY(u) + return new AST\SimpleSelectExpression($this->FunctionDeclaration()); + + default: + // Do nothing + } + break; + + case Lexer::T_OPEN_PARENTHESIS: + if ($peek['type'] !== Lexer::T_SELECT) { + // Shortcut: ScalarExpression => SimpleArithmeticExpression + $expression = $this->SimpleArithmeticExpression(); + + return new AST\SimpleSelectExpression($expression); + } + + // Subselect + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing + } + + $this->lexer->peek(); + + $expression = $this->ScalarExpression(); + $expr = new AST\SimpleSelectExpression($expression); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; + $resultVariable = $this->AliasResultVariable(); + $expr->fieldIdentificationVariable = $resultVariable; + + // Include AliasResultVariable in query components. + $this->queryComponents[$resultVariable] = array( + 'resultvariable' => $expr, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + } + + return $expr; + } + + /** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalExpression + */ + public function ConditionalExpression() + { + $conditionalTerms = array(); + $conditionalTerms[] = $this->ConditionalTerm(); + + while ($this->lexer->isNextToken(Lexer::T_OR)) { + $this->match(Lexer::T_OR); + + $conditionalTerms[] = $this->ConditionalTerm(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalExpression + // if only one AST\ConditionalTerm is defined + if (count($conditionalTerms) == 1) { + return $conditionalTerms[0]; + } + + return new AST\ConditionalExpression($conditionalTerms); + } + + /** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalTerm + */ + public function ConditionalTerm() + { + $conditionalFactors = array(); + $conditionalFactors[] = $this->ConditionalFactor(); + + while ($this->lexer->isNextToken(Lexer::T_AND)) { + $this->match(Lexer::T_AND); + + $conditionalFactors[] = $this->ConditionalFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalTerm + // if only one AST\ConditionalFactor is defined + if (count($conditionalFactors) == 1) { + return $conditionalFactors[0]; + } + + return new AST\ConditionalTerm($conditionalFactors); + } + + /** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @return \Doctrine\ORM\Query\AST\ConditionalFactor + */ + public function ConditionalFactor() + { + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $conditionalPrimary = $this->ConditionalPrimary(); + + // Phase 1 AST optimization: Prevent AST\ConditionalFactor + // if only one AST\ConditionalPrimary is defined + if ( ! $not) { + return $conditionalPrimary; + } + + $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary); + $conditionalFactor->not = $not; + + return $conditionalFactor; + } + + /** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @return \Doctrine\ORM\Query\AST\ConditionalPrimary + */ + public function ConditionalPrimary() + { + $condPrimary = new AST\ConditionalPrimary; + + if ( ! $this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + // Peek beyond the matching closing parenthesis ')' + $peek = $this->peekBeyondClosingParenthesis(); + + if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || + in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) || + $this->isMathOperator($peek)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + $condPrimary->conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $condPrimary; + } + + /** + * SimpleConditionalExpression ::= + * ComparisonExpression | BetweenExpression | LikeExpression | + * InExpression | NullComparisonExpression | ExistsExpression | + * EmptyCollectionComparisonExpression | CollectionMemberExpression | + * InstanceOfExpression + */ + public function SimpleConditionalExpression() + { + if ($this->lexer->isNextToken(Lexer::T_EXISTS)) { + return $this->ExistsExpression(); + } + + $token = $this->lexer->lookahead; + $peek = $this->lexer->glimpse(); + $lookahead = $token; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->lexer->glimpse(); + } + + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) { + // Peek beyond the matching closing parenthesis. + $beyond = $this->lexer->peek(); + + switch ($peek['value']) { + case '(': + //Peeks beyond the matched closing parenthesis. + $token = $this->peekBeyondClosingParenthesis(false); + + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + if ($token['type'] === Lexer::T_IS) { + $lookahead = $this->lexer->peek(); + } + break; + + default: + // Peek beyond the PathExpression or InputParameter. + $token = $beyond; + + while ($token['value'] === '.') { + $this->lexer->peek(); + + $token = $this->lexer->peek(); + } + + // Also peek beyond a NOT if there is one. + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + // We need to go even further in case of IS (differentiate between NULL and EMPTY) + $lookahead = $this->lexer->peek(); + } + + // Also peek beyond a NOT if there is one. + if ($lookahead['type'] === Lexer::T_NOT) { + $lookahead = $this->lexer->peek(); + } + + $this->lexer->resetPeek(); + } + + if ($token['type'] === Lexer::T_BETWEEN) { + return $this->BetweenExpression(); + } + + if ($token['type'] === Lexer::T_LIKE) { + return $this->LikeExpression(); + } + + if ($token['type'] === Lexer::T_IN) { + return $this->InExpression(); + } + + if ($token['type'] === Lexer::T_INSTANCE) { + return $this->InstanceOfExpression(); + } + + if ($token['type'] === Lexer::T_MEMBER) { + return $this->CollectionMemberExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_EMPTY) { + return $this->EmptyCollectionComparisonExpression(); + } + + return $this->ComparisonExpression(); + } + + /** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression + */ + public function EmptyCollectionComparisonExpression() + { + $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression( + $this->CollectionValuedPathExpression() + ); + $this->match(Lexer::T_IS); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $emptyCollectionCompExpr->not = true; + } + + $this->match(Lexer::T_EMPTY); + + return $emptyCollectionCompExpr; + } + + /** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression + */ + public function CollectionMemberExpression() + { + $not = false; + $entityExpr = $this->EntityExpression(); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $this->match(Lexer::T_MEMBER); + + if ($this->lexer->isNextToken(Lexer::T_OF)) { + $this->match(Lexer::T_OF); + } + + $collMemberExpr = new AST\CollectionMemberExpression( + $entityExpr, $this->CollectionValuedPathExpression() + ); + $collMemberExpr->not = $not; + + return $collMemberExpr; + } + + /** + * Literal ::= string | char | integer | float | boolean + * + * @return string + */ + public function Literal() + { + switch ($this->lexer->lookahead['type']) { + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + + case Lexer::T_INTEGER: + case Lexer::T_FLOAT: + $this->match( + $this->lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT + ); + return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token['value']); + + case Lexer::T_TRUE: + case Lexer::T_FALSE: + $this->match( + $this->lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE + ); + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); + + default: + $this->syntaxError('Literal'); + } + } + + /** + * InParameter ::= Literal | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function InParameter() + { + if ($this->lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) { + return $this->InputParameter(); + } + + return $this->Literal(); + } + + /** + * InputParameter ::= PositionalParameter | NamedParameter + * + * @return \Doctrine\ORM\Query\AST\InputParameter + */ + public function InputParameter() + { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + /** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ArithmeticExpression + */ + public function ArithmeticExpression() + { + $expr = new AST\ArithmeticExpression; + + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->lexer->glimpse(); + + if ($peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr->subselect = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression(); + + return $expr; + } + + /** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public function SimpleArithmeticExpression() + { + $terms = array(); + $terms[] = $this->ArithmeticTerm(); + + while (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + + $terms[] = $this->lexer->token['value']; + $terms[] = $this->ArithmeticTerm(); + } + + // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression + // if only one AST\ArithmeticTerm is defined + if (count($terms) == 1) { + return $terms[0]; + } + + return new AST\SimpleArithmeticExpression($terms); + } + + /** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @return \Doctrine\ORM\Query\AST\ArithmeticTerm + */ + public function ArithmeticTerm() + { + $factors = array(); + $factors[] = $this->ArithmeticFactor(); + + while (($isMult = $this->lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->lexer->isNextToken(Lexer::T_DIVIDE)) { + $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE); + + $factors[] = $this->lexer->token['value']; + $factors[] = $this->ArithmeticFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ArithmeticTerm + // if only one AST\ArithmeticFactor is defined + if (count($factors) == 1) { + return $factors[0]; + } + + return new AST\ArithmeticTerm($factors); + } + + /** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @return \Doctrine\ORM\Query\AST\ArithmeticFactor + */ + public function ArithmeticFactor() + { + $sign = null; + + if (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + $sign = $isPlus; + } + + $primary = $this->ArithmeticPrimary(); + + // Phase 1 AST optimization: Prevent AST\ArithmeticFactor + // if only one AST\ArithmeticPrimary is defined + if ($sign === null) { + return $primary; + } + + return new AST\ArithmeticFactor($primary, $sign); + } + + /** + * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression + * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + * | InputParameter | CaseExpression + */ + public function ArithmeticPrimary() + { + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $expr = $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\ParenthesisExpression($expr); + } + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + case Lexer::T_CASE: + return $this->CaseExpression(); + + case Lexer::T_IDENTIFIER: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '(') { + return $this->FunctionDeclaration(); + } + + if ($peek['value'] == '.') { + return $this->SingleValuedPathExpression(); + } + + if (isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); + } + + return $this->StateFieldPathExpression(); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + default: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '(') { + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } + + return $this->Literal(); + } + } + + /** + * StringExpression ::= StringPrimary | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\StringPrimary | + * \Doctrine\ORM\Query\AST\Subselect + */ + public function StringExpression() + { + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->lexer->glimpse(); + + if ($peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + return $this->StringPrimary(); + } + + /** + * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + */ + public function StringPrimary() + { + $lookaheadType = $this->lexer->lookahead['type']; + + switch ($lookaheadType) { + case Lexer::T_IDENTIFIER: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '.') { + return $this->StateFieldPathExpression(); + } + + if ($peek['value'] == '(') { + // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions. + return $this->FunctionDeclaration(); + } + + $this->syntaxError("'.' or '('"); + break; + + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + return $this->CaseExpression(); + + default: + if ($this->isAggregateFunction($lookaheadType)) { + return $this->AggregateExpression(); + } + } + + $this->syntaxError( + 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression' + ); + } + + /** + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * + * @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression | + * \Doctrine\ORM\Query\AST\SimpleEntityExpression + */ + public function EntityExpression() + { + $glimpse = $this->lexer->glimpse(); + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') { + return $this->SingleValuedAssociationPathExpression(); + } + + return $this->SimpleEntityExpression(); + } + + /** + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function SimpleEntityExpression() + { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + return $this->InputParameter(); + } + + return $this->StateFieldPathExpression(); + } + + /** + * AggregateExpression ::= + * ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" | + * "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")" + * + * @return \Doctrine\ORM\Query\AST\AggregateExpression + */ + public function AggregateExpression() + { + $lookaheadType = $this->lexer->lookahead['type']; + $isDistinct = false; + + if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) { + $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); + } + + $this->match($lookaheadType); + $functionName = $this->lexer->token['value']; + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + $isDistinct = true; + } + + $pathExp = ($lookaheadType === Lexer::T_COUNT) + ? $this->SingleValuedPathExpression() + : $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); + } + + /** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\QuantifiedExpression + */ + public function QuantifiedExpression() + { + $lookaheadType = $this->lexer->lookahead['type']; + $value = $this->lexer->lookahead['value']; + + if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) { + $this->syntaxError('ALL, ANY or SOME'); + } + + $this->match($lookaheadType); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $qExpr = new AST\QuantifiedExpression($this->Subselect()); + $qExpr->type = $value; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $qExpr; + } + + /** + * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + * + * @return \Doctrine\ORM\Query\AST\BetweenExpression + */ + public function BetweenExpression() + { + $not = false; + $arithExpr1 = $this->ArithmeticExpression(); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_BETWEEN); + $arithExpr2 = $this->ArithmeticExpression(); + $this->match(Lexer::T_AND); + $arithExpr3 = $this->ArithmeticExpression(); + + $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3); + $betweenExpr->not = $not; + + return $betweenExpr; + } + + /** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + * + * @return \Doctrine\ORM\Query\AST\ComparisonExpression + */ + public function ComparisonExpression() + { + $this->lexer->glimpse(); + + $leftExpr = $this->ArithmeticExpression(); + $operator = $this->ComparisonOperator(); + $rightExpr = ($this->isNextAllAnySome()) + ? $this->QuantifiedExpression() + : $this->ArithmeticExpression(); + + return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr); + } + + /** + * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + * + * @return \Doctrine\ORM\Query\AST\InExpression + */ + public function InExpression() + { + $inExpression = new AST\InExpression($this->ArithmeticExpression()); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $inExpression->not = true; + } + + $this->match(Lexer::T_IN); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->lexer->isNextToken(Lexer::T_SELECT)) { + $inExpression->subselect = $this->Subselect(); + } else { + $literals = array(); + $literals[] = $this->InParameter(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $literals[] = $this->InParameter(); + } + + $inExpression->literals = $literals; + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $inExpression; + } + + /** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * + * @return \Doctrine\ORM\Query\AST\InstanceOfExpression + */ + public function InstanceOfExpression() + { + $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable()); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $instanceOfExpression->not = true; + } + + $this->match(Lexer::T_INSTANCE); + $this->match(Lexer::T_OF); + + $exprValues = array(); + + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $exprValues[] = $this->InstanceOfParameter(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $exprValues[] = $this->InstanceOfParameter(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + $exprValues[] = $this->InstanceOfParameter(); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + /** + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @return mixed + */ + public function InstanceOfParameter() + { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + return $this->AliasIdentificationVariable(); + } + + /** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] + * + * @return \Doctrine\ORM\Query\AST\LikeExpression + */ + public function LikeExpression() + { + $stringExpr = $this->StringExpression(); + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_LIKE); + + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + $stringPattern = new AST\InputParameter($this->lexer->token['value']); + } else { + $stringPattern = $this->StringPrimary(); + } + + $escapeChar = null; + + if ($this->lexer->lookahead['type'] === Lexer::T_ESCAPE) { + $this->match(Lexer::T_ESCAPE); + $this->match(Lexer::T_STRING); + + $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + } + + $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar); + $likeExpr->not = $not; + + return $likeExpr; + } + + /** + * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | SingleValuedPathExpression) "IS" ["NOT"] "NULL" + * + * @return \Doctrine\ORM\Query\AST\NullComparisonExpression + */ + public function NullComparisonExpression() + { + switch (true) { + case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER): + $this->match(Lexer::T_INPUT_PARAMETER); + + $expr = new AST\InputParameter($this->lexer->token['value']); + break; + + case $this->lexer->isNextToken(Lexer::T_NULLIF): + $expr = $this->NullIfExpression(); + break; + + case $this->lexer->isNextToken(Lexer::T_COALESCE): + $expr = $this->CoalesceExpression(); + break; + + case $this->isFunction(): + $expr = $this->FunctionDeclaration(); + break; + + default: + $expr = $this->SingleValuedPathExpression(); + break; + } + + $nullCompExpr = new AST\NullComparisonExpression($expr); + + $this->match(Lexer::T_IS); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $nullCompExpr->not = true; + } + + $this->match(Lexer::T_NULL); + + return $nullCompExpr; + } + + /** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ExistsExpression + */ + public function ExistsExpression() + { + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_EXISTS); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $existsExpression = new AST\ExistsExpression($this->Subselect()); + $existsExpression->not = $not; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $existsExpression; + } + + /** + * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + * + * @return string + */ + public function ComparisonOperator() + { + switch ($this->lexer->lookahead['value']) { + case '=': + $this->match(Lexer::T_EQUALS); + + return '='; + + case '<': + $this->match(Lexer::T_LOWER_THAN); + $operator = '<'; + + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } else if ($this->lexer->isNextToken(Lexer::T_GREATER_THAN)) { + $this->match(Lexer::T_GREATER_THAN); + $operator .= '>'; + } + + return $operator; + + case '>': + $this->match(Lexer::T_GREATER_THAN); + $operator = '>'; + + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } + + return $operator; + + case '!': + $this->match(Lexer::T_NEGATE); + $this->match(Lexer::T_EQUALS); + + return '<>'; + + default: + $this->syntaxError('=, <, <=, <>, >, >=, !='); + } + } + + /** + * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionDeclaration() + { + $token = $this->lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for built-in functions first! + switch (true) { + case (isset(self::$_STRING_FUNCTIONS[$funcName])): + return $this->FunctionsReturningStrings(); + + case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])): + return $this->FunctionsReturningNumerics(); + + case (isset(self::$_DATETIME_FUNCTIONS[$funcName])): + return $this->FunctionsReturningDatetime(); + + default: + return $this->CustomFunctionDeclaration(); + } + } + + /** + * Helper function for FunctionDeclaration grammar rule. + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + private function CustomFunctionDeclaration() + { + $token = $this->lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for custom functions afterwards + $config = $this->em->getConfiguration(); + + switch (true) { + case ($config->getCustomStringFunction($funcName) !== null): + return $this->CustomFunctionsReturningStrings(); + + case ($config->getCustomNumericFunction($funcName) !== null): + return $this->CustomFunctionsReturningNumerics(); + + case ($config->getCustomDatetimeFunction($funcName) !== null): + return $this->CustomFunctionsReturningDatetime(); + + default: + $this->syntaxError('known function', $token); + } + } + + /** + * FunctionsReturningNumerics ::= + * "LENGTH" "(" StringPrimary ")" | + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + * "ABS" "(" SimpleArithmeticExpression ")" | + * "SQRT" "(" SimpleArithmeticExpression ")" | + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "SIZE" "(" CollectionValuedPathExpression ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningNumerics() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningNumerics() + { + // getCustomNumericFunction is case-insensitive + $funcName = strtolower($this->lexer->lookahead['value']); + $funcClass = $this->em->getConfiguration()->getCustomNumericFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningDatetime() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningDatetime() + { + // getCustomDatetimeFunction is case-insensitive + $funcName = $this->lexer->lookahead['value']; + $funcClass = $this->em->getConfiguration()->getCustomDatetimeFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningStrings ::= + * "CONCAT" "(" StringPrimary "," StringPrimary ")" | + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + * "LOWER" "(" StringPrimary ")" | + * "UPPER" "(" StringPrimary ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningStrings() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningStrings() + { + // getCustomStringFunction is case-insensitive + $funcName = $this->lexer->lookahead['value']; + $funcClass = $this->em->getConfiguration()->getCustomStringFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php new file mode 100644 index 00000000..dfd7dd2c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Encapsulates the resulting components from a DQL query parsing process that + * can be serialized. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class ParserResult +{ + /** + * The SQL executor used for executing the SQL. + * + * @var \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + private $_sqlExecutor; + + /** + * The ResultSetMapping that describes how to map the SQL result set. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $_resultSetMapping; + + /** + * The mappings of DQL parameter names/positions to SQL parameter positions. + * + * @var array + */ + private $_parameterMappings = array(); + + /** + * Initializes a new instance of the ParserResult class. + * The new instance is initialized with an empty ResultSetMapping. + */ + public function __construct() + { + $this->_resultSetMapping = new ResultSetMapping; + } + + /** + * Gets the ResultSetMapping for the parsed query. + * + * @return ResultSetMapping|null The result set mapping of the parsed query or NULL + * if the query is not a SELECT query. + */ + public function getResultSetMapping() + { + return $this->_resultSetMapping; + } + + /** + * Sets the ResultSetMapping of the parsed query. + * + * @param ResultSetMapping $rsm + * + * @return void + */ + public function setResultSetMapping(ResultSetMapping $rsm) + { + $this->_resultSetMapping = $rsm; + } + + /** + * Sets the SQL executor that should be used for this ParserResult. + * + * @param \Doctrine\ORM\Query\Exec\AbstractSqlExecutor $executor + * + * @return void + */ + public function setSqlExecutor($executor) + { + $this->_sqlExecutor = $executor; + } + + /** + * Gets the SQL executor used by this ParserResult. + * + * @return \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + public function getSqlExecutor() + { + return $this->_sqlExecutor; + } + + /** + * Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to + * several SQL parameter positions. + * + * @param string|integer $dqlPosition + * @param integer $sqlPosition + * + * @return void + */ + public function addParameterMapping($dqlPosition, $sqlPosition) + { + $this->_parameterMappings[$dqlPosition][] = $sqlPosition; + } + + /** + * Gets all DQL to SQL parameter mappings. + * + * @return array The parameter mappings. + */ + public function getParameterMappings() + { + return $this->_parameterMappings; + } + + /** + * Gets the SQL parameter positions for a DQL parameter name/position. + * + * @param string|integer $dqlPosition The name or position of the DQL parameter. + * + * @return array The positions of the corresponding SQL parameters. + */ + public function getSqlParameterPositions($dqlPosition) + { + return $this->_parameterMappings[$dqlPosition]; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php new file mode 100644 index 00000000..ffb45753 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A parse tree printer for Doctrine Query Language parser. + * + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.phpdoctrine.org + * @since 2.0 + */ +class Printer +{ + /** + * Current indentation level + * + * @var int + */ + protected $_indent = 0; + + /** + * Defines whether parse tree is printed (default, false) or not (true). + * + * @var bool + */ + protected $_silent; + + /** + * Constructs a new parse tree printer. + * + * @param bool $silent Parse tree will not be printed if true. + */ + public function __construct($silent = false) + { + $this->_silent = $silent; + } + + /** + * Prints an opening parenthesis followed by production name and increases + * indentation level by one. + * + * This method is called before executing a production. + * + * @param string $name Production name. + * + * @return void + */ + public function startProduction($name) + { + $this->println('(' . $name); + $this->_indent++; + } + + /** + * Decreases indentation level by one and prints a closing parenthesis. + * + * This method is called after executing a production. + * + * @return void + */ + public function endProduction() + { + $this->_indent--; + $this->println(')'); + } + + /** + * Prints text indented with spaces depending on current indentation level. + * + * @param string $str The text. + * + * @return void + */ + public function println($str) + { + if ( ! $this->_silent) { + echo str_repeat(' ', $this->_indent), $str, "\n"; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php new file mode 100644 index 00000000..da0d2d50 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Description of QueryException. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class QueryException extends \Doctrine\ORM\ORMException +{ + /** + * @param string $dql + * + * @return QueryException + */ + public static function dqlError($dql) + { + return new self($dql); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function syntaxError($message, $previous = null) + { + return new self('[Syntax Error] ' . $message, 0, $previous); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function semanticalError($message, $previous = null) + { + return new self('[Semantical Error] ' . $message, 0, $previous); + } + + /** + * @return QueryException + */ + public static function invalidLockMode() + { + return new self('Invalid lock mode hint provided.'); + } + + /** + * @param string $expected + * @param string $received + * + * @return QueryException + */ + public static function invalidParameterType($expected, $received) + { + return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); + } + + /** + * @param string $pos + * + * @return QueryException + */ + public static function invalidParameterPosition($pos) + { + return new self('Invalid parameter position: ' . $pos); + } + + /** + * @return QueryException + */ + public static function invalidParameterNumber() + { + return new self("Invalid parameter number: number of bound variables does not match number of tokens"); + } + + /** + * @param string $value + * + * @return QueryException + */ + public static function invalidParameterFormat($value) + { + return new self('Invalid parameter format, '.$value.' given, but : or ? expected.'); + } + + /** + * @param string $key + * + * @return QueryException + */ + public static function unknownParameter($key) + { + return new self("Invalid parameter: token ".$key." is not defined in the query."); + } + + /** + * @return QueryException + */ + public static function parameterTypeMismatch() + { + return new self("DQL Query parameter and type numbers mismatch, but have to be exactly equal."); + } + + /** + * @param object $pathExpr + * + * @return QueryException + */ + public static function invalidPathExpression($pathExpr) + { + return new self( + "Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'." + ); + } + + /** + * @param string $literal + * + * @return QueryException + */ + public static function invalidLiteral($literal) + { + return new self("Invalid literal '$literal'"); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + { + return new self( + "Invalid query operation: Not allowed to iterate over fetch join collections ". + "in class ".$assoc['sourceEntity']." association ".$assoc['fieldName'] + ); + } + + /** + * @return QueryException + */ + public static function partialObjectsAreDangerous() + { + return new self( + "Loading partial objects is dangerous. Fetch full objects or consider " . + "using a different fetch mode. If you really want partial objects, " . + "set the doctrine.forcePartialLoad query hint to TRUE." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function overwritingJoinConditionsNotYetSupported($assoc) + { + return new self( + "Unsupported query operation: It is not yet possible to overwrite the join ". + "conditions in class ".$assoc['sourceEntityName']." association ".$assoc['fieldName'].". ". + "Use WITH to append additional join conditions to the association." + ); + } + + /** + * @return QueryException + */ + public static function associationPathInverseSideNotSupported() + { + return new self( + "A single-valued association path expression to an inverse side is not supported". + " in DQL queries. Use an explicit join instead." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinNotAllowed($assoc) + { + return new self( + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." + ); + } + + /** + * @return QueryException + */ + public static function associationPathCompositeKeyNotSupported() + { + return new self( + "A single-valued association path expression to an entity with a composite primary ". + "key is not supported. Explicitly name the components of the composite primary key ". + "in the query." + ); + } + + /** + * @param string $className + * @param string $rootClass + * + * @return QueryException + */ + public static function instanceOfUnrelatedClass($className, $rootClass) + { + return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . + "inheritance hierarchy exists between these two classes."); + } + + /** + * @param string $dqlAlias + * + * @return QueryException + */ + public static function invalidQueryComponent($dqlAlias) + { + return new self( + "Invalid query component given for DQL alias '" . $dqlAlias . "', ". + "requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys." + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php new file mode 100644 index 00000000..775d7001 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -0,0 +1,179 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query\Parameter; + +/** + * Converts Collection expressions to Query expressions. + * + * @author Kirill chEbba Chebunin + * @since 2.4 + */ +class QueryExpressionVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private static $operatorMap = array( + Comparison::GT => Expr\Comparison::GT, + Comparison::GTE => Expr\Comparison::GTE, + Comparison::LT => Expr\Comparison::LT, + Comparison::LTE => Expr\Comparison::LTE + ); + + /** + * @var Expr + */ + private $expr; + + /** + * @var array + */ + private $parameters = array(); + + /** + * Constructor with internal initialization. + */ + public function __construct() + { + $this->expr = new Expr(); + } + + /** + * Gets bound parameters. + * Filled after {@link dispach()}. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getParameters() + { + return new ArrayCollection($this->parameters); + } + + /** + * Clears parameters. + * + * @return void + */ + public function clearParameters() + { + $this->parameters = array(); + } + + /** + * Converts Criteria expression to Query one based on static map. + * + * @param string $criteriaOperator + * + * @return string|null + */ + private static function convertComparisonOperator($criteriaOperator) + { + return isset(self::$operatorMap[$criteriaOperator]) ? self::$operatorMap[$criteriaOperator] : null; + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return new Expr\Andx($expressionList); + + case CompositeExpression::TYPE_OR: + return new Expr\Orx($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $parameterName = str_replace('.', '_', $comparison->getField()); + $parameter = new Parameter($parameterName, $this->walkValue($comparison->getValue())); + $placeholder = ':' . $parameterName; + + switch ($comparison->getOperator()) { + case Comparison::IN: + $this->parameters[] = $parameter; + return $this->expr->in($comparison->getField(), $placeholder); + + case Comparison::NIN: + $this->parameters[] = $parameter; + return $this->expr->notIn($comparison->getField(), $placeholder); + + case Comparison::EQ: + case Comparison::IS: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNull($comparison->getField()); + } + $this->parameters[] = $parameter; + return $this->expr->eq($comparison->getField(), $placeholder); + + case Comparison::NEQ: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNotNull($comparison->getField()); + } + $this->parameters[] = $parameter; + return $this->expr->neq($comparison->getField(), $placeholder); + + default: + $operator = self::convertComparisonOperator($comparison->getOperator()); + if ($operator) { + $this->parameters[] = $parameter; + return new Expr\Comparison( + $comparison->getField(), + $operator, + $placeholder + ); + } + + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php new file mode 100644 index 00000000..b06b6838 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -0,0 +1,547 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result. + * + * IMPORTANT NOTE: + * The properties of this class are only public for fast internal READ access and to (drastically) + * reduce the size of serialized instances for more effective caching due to better (un-)serialization + * performance. + * + * Users should use the public methods. + * + * @author Roman Borschel + * @since 2.0 + * @todo Think about whether the number of lookup maps can be reduced. + */ +class ResultSetMapping +{ + /** + * Whether the result is mixed (contains scalar values together with field values). + * + * @ignore + * @var boolean + */ + public $isMixed = false; + + /** + * Maps alias names to class names. + * + * @ignore + * @var array + */ + public $aliasMap = array(); + + /** + * Maps alias names to related association field names. + * + * @ignore + * @var array + */ + public $relationMap = array(); + + /** + * Maps alias names to parent alias names. + * + * @ignore + * @var array + */ + public $parentAliasMap = array(); + + /** + * Maps column names in the result set to field names for each class. + * + * @ignore + * @var array + */ + public $fieldMappings = array(); + + /** + * Maps column names in the result set to the alias/field name to use in the mapped result. + * + * @ignore + * @var array + */ + public $scalarMappings = array(); + + /** + * Maps column names in the result set to the alias/field type to use in the mapped result. + * + * @ignore + * @var array + */ + public $typeMappings = array(); + + /** + * Maps entities in the result set to the alias name to use in the mapped result. + * + * @ignore + * @var array + */ + public $entityMappings = array(); + + /** + * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. + * + * @ignore + * @var array + */ + public $metaMappings = array(); + + /** + * Maps column names in the result set to the alias they belong to. + * + * @ignore + * @var array + */ + public $columnOwnerMap = array(); + + /** + * List of columns in the result set that are used as discriminator columns. + * + * @ignore + * @var array + */ + public $discriminatorColumns = array(); + + /** + * Maps alias names to field names that should be used for indexing. + * + * @ignore + * @var array + */ + public $indexByMap = array(); + + /** + * Map from column names to class names that declare the field the column is mapped to. + * + * @ignore + * @var array + */ + public $declaringClasses = array(); + + /** + * This is necessary to hydrate derivate foreign keys correctly. + * + * @var array + */ + public $isIdentifierColumn = array(); + + /** + * Maps column names in the result set to field names for each new object expression. + * + * @var array + */ + public $newObjectMappings = array(); + + /** + * Adds an entity result to this ResultSetMapping. + * + * @param string $class The class name of the entity. + * @param string $alias The alias for the class. The alias must be unique among all entity + * results or joined entity results within this ResultSetMapping. + * @param string|null $resultAlias The result alias with which the entity result should be + * placed in the result structure. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addRootEntity + */ + public function addEntityResult($class, $alias, $resultAlias = null) + { + $this->aliasMap[$alias] = $class; + $this->entityMappings[$alias] = $resultAlias; + + if ($resultAlias !== null) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Sets a discriminator column for an entity result or joined entity result. + * The discriminator column will be used to determine the concrete class name to + * instantiate. + * + * @param string $alias The alias of the entity result or joined entity result the discriminator + * column should be used for. + * @param string $discrColumn The name of the discriminator column in the SQL result set. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addDiscriminatorColumn + */ + public function setDiscriminatorColumn($alias, $discrColumn) + { + $this->discriminatorColumns[$alias] = $discrColumn; + $this->columnOwnerMap[$discrColumn] = $alias; + + return $this; + } + + /** + * Sets a field to use for indexing an entity result or joined entity result. + * + * @param string $alias The alias of an entity result or joined entity result. + * @param string $fieldName The name of the field to use for indexing. + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexBy($alias, $fieldName) + { + $found = false; + + foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) { + if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue; + + $this->addIndexByColumn($alias, $columnName); + $found = true; + + break; + } + + /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals + if ( ! $found) { + $message = sprintf( + 'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.', + $alias, + $fieldName + ); + + throw new \LogicException($message); + } + */ + + return $this; + } + + /** + * Sets to index by a scalar result column name. + * + * @param string $resultColumnName + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByScalar($resultColumnName) + { + $this->indexByMap['scalars'] = $resultColumnName; + + return $this; + } + + /** + * Sets a column to use for indexing an entity or joined entity result by the given alias name. + * + * @param string $alias + * @param string $resultColumnName + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByColumn($alias, $resultColumnName) + { + $this->indexByMap[$alias] = $resultColumnName; + + return $this; + } + + /** + * Checks whether an entity result or joined entity result with a given alias has + * a field set for indexing. + * + * @param string $alias + * + * @return boolean + * + * @todo Rename: isIndexed($alias) + */ + public function hasIndexBy($alias) + { + return isset($this->indexByMap[$alias]); + } + + /** + * Checks whether the column with the given name is mapped as a field result + * as part of an entity result or joined entity result. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return boolean + * + * @todo Rename: isField + */ + public function isFieldResult($columnName) + { + return isset($this->fieldMappings[$columnName]); + } + + /** + * Adds a field to the result that belongs to an entity or joined entity. + * + * @param string $alias The alias of the root entity or joined entity to which the field belongs. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. + * @param string|null $declaringClass The name of the class that declares/owns the specified field. + * When $alias refers to a superclass in a mapped hierarchy but + * the field $fieldName is defined on a subclass, specify that here. + * If not specified, the field is assumed to belong to the class + * designated by $alias. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addField + */ + public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null) + { + // column name (in result set) => field name + $this->fieldMappings[$columnName] = $fieldName; + // column name => alias of owner + $this->columnOwnerMap[$columnName] = $alias; + // field name => class name of declaring class + $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias]; + + if ( ! $this->isMixed && $this->scalarMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Adds a joined entity result. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result + * with the joined entity result. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addJoinedEntity + */ + public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) + { + $this->aliasMap[$alias] = $class; + $this->parentAliasMap[$alias] = $parentAlias; + $this->relationMap[$alias] = $relation; + + return $this; + } + + /** + * Adds a scalar result mapping. + * + * @param string $columnName The name of the column in the SQL result set. + * @param string $alias The result alias with which the scalar result should be placed in the result structure. + * @param string $type The column type + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addScalar + */ + public function addScalarResult($columnName, $alias, $type = 'string') + { + $this->scalarMappings[$columnName] = $alias; + $this->typeMappings[$columnName] = $type; + + if ( ! $this->isMixed && $this->fieldMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Checks whether a column with a given name is mapped as a scalar result. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return boolean + * + * @todo Rename: isScalar + */ + public function isScalarResult($columnName) + { + return isset($this->scalarMappings[$columnName]); + } + + /** + * Gets the name of the class of an entity result or joined entity result, + * identified by the given unique alias. + * + * @param string $alias + * + * @return string + */ + public function getClassName($alias) + { + return $this->aliasMap[$alias]; + } + + /** + * Gets the field alias for a column that is mapped as a scalar value. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return string + */ + public function getScalarAlias($columnName) + { + return $this->scalarMappings[$columnName]; + } + + /** + * Gets the name of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * + * @return string + */ + public function getDeclaringClass($columnName) + { + return $this->declaringClasses[$columnName]; + } + + /** + * @param string $alias + * + * @return AssociationMapping + */ + public function getRelation($alias) + { + return $this->relationMap[$alias]; + } + + /** + * @param string $alias + * + * @return boolean + */ + public function isRelation($alias) + { + return isset($this->relationMap[$alias]); + } + + /** + * Gets the alias of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * + * @return string + */ + public function getEntityAlias($columnName) + { + return $this->columnOwnerMap[$columnName]; + } + + /** + * Gets the parent alias of the given alias. + * + * @param string $alias + * + * @return string + */ + public function getParentAlias($alias) + { + return $this->parentAliasMap[$alias]; + } + + /** + * Checks whether the given alias has a parent alias. + * + * @param string $alias + * + * @return boolean + */ + public function hasParentAlias($alias) + { + return isset($this->parentAliasMap[$alias]); + } + + /** + * Gets the field name for a column name. + * + * @param string $columnName + * + * @return string + */ + public function getFieldName($columnName) + { + return $this->fieldMappings[$columnName]; + } + + /** + * @return array + */ + public function getAliasMap() + { + return $this->aliasMap; + } + + /** + * Gets the number of different entities that appear in the mapped result. + * + * @return integer + */ + public function getEntityResultCount() + { + return count($this->aliasMap); + } + + /** + * Checks whether this ResultSetMapping defines a mixed result. + * + * Mixed results can only occur in object and array (graph) hydration. In such a + * case a mixed result means that scalar values are mixed with objects/array in + * the result. + * + * @return boolean + */ + public function isMixedResult() + { + return $this->isMixed; + } + + /** + * Adds a meta column (foreign key or discriminator column) to the result set. + * + * @param string $alias + * @param string $columnName + * @param string $fieldName + * @param bool $isIdentifierColumn + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false) + { + $this->metaMappings[$columnName] = $fieldName; + $this->columnOwnerMap[$columnName] = $alias; + + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } + + return $this; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php new file mode 100644 index 00000000..da7ce256 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,443 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields. + * + * @author Michael Ridgway + * @since 2.1 + */ +class ResultSetMappingBuilder extends ResultSetMapping +{ + /** + * Picking this rename mode will register entity columns as is, + * as they are in the database. This can cause clashes when multiple + * entities are fetched that have columns with the same name. + * + * @var int + */ + const COLUMN_RENAMING_NONE = 1; + + /** + * Picking custom renaming allows the user to define the renaming + * of specific columns with a rename array that contains column names as + * keys and result alias as values. + * + * @var int + */ + const COLUMN_RENAMING_CUSTOM = 2; + + /** + * Incremental renaming uses a result set mapping internal counter to add a + * number to each column result, leading to uniqueness. This only works if + * you use {@see generateSelectClause()} to generate the SELECT clause for + * you. + * + * @var int + */ + const COLUMN_RENAMING_INCREMENT = 3; + + /** + * @var int + */ + private $sqlCounter = 0; + + /** + * @var EntityManager + */ + private $em; + + /** + * Default column renaming mode. + * + * @var int + */ + private $defaultRenameMode; + + /** + * @param EntityManager $em + * @param integer $defaultRenameMode + */ + public function __construct(EntityManager $em, $defaultRenameMode = self::COLUMN_RENAMING_NONE) + { + $this->em = $em; + $this->defaultRenameMode = $defaultRenameMode; + } + + /** + * Adds a root entity and all of its fields to the result set. + * + * @param string $class The class name of the root entity. + * @param string $alias The unique alias to use for the root entity. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addEntityResult($class, $alias); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * Adds a joined entity and all of its fields to the result set. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result + * with the joined entity result. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * Adds all fields of the given class to the result set mapping (columns and meta fields). + * + * @param string $class + * @param string $alias + * @param array $columnAliasMap + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function addAllClassFields($class, $alias, $columnAliasMap = array()) + { + $classMetadata = $this->em->getClassMetadata($class); + $platform = $this->em->getConnection()->getDatabasePlatform(); + + if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.'); + } + + + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->fieldMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + + $this->addFieldResult($alias, $columnAlias, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->metaMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper."); + } + + $this->addMetaResult($alias, $columnAlias, $columnName); + } + } + } + } + + /** + * Gets column alias for a given column. + * + * @param string $columnName + * @param int $mode + * @param array $customRenameColumns + * + * @return string + */ + private function getColumnAlias($columnName, $mode, array $customRenameColumns) + { + switch ($mode) { + case self::COLUMN_RENAMING_INCREMENT: + return $columnName . $this->sqlCounter++; + + case self::COLUMN_RENAMING_CUSTOM: + return isset($customRenameColumns[$columnName]) + ? $customRenameColumns[$columnName] : $columnName; + + case self::COLUMN_RENAMING_NONE: + return $columnName; + + } + } + + /** + * Retrieves a class columns and join columns aliases that are used in the SELECT clause. + * + * This depends on the renaming mode selected by the user. + * + * @param string $className + * @param int $mode + * @param array $customRenameColumns + * + * @return array + */ + private function getColumnAliasMap($className, $mode, array $customRenameColumns) + { + if ($customRenameColumns) { // for BC with 2.2-2.3 API + $mode = self::COLUMN_RENAMING_CUSTOM; + } + + $columnAlias = array(); + $class = $this->em->getClassMetadata($className); + + foreach ($class->getColumnNames() as $columnName) { + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + + foreach ($class->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + } + } + + return $columnAlias; + } + + /** + * Adds the mappings of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param array $queryMapping + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping) + { + if (isset($queryMapping['resultClass'])) { + return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']); + } + + return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']); + } + + /** + * Adds the class mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultClassName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName) + { + $classMetadata = $this->em->getClassMetadata($resultClassName); + $shortName = $classMetadata->reflClass->getShortName(); + $alias = strtolower($shortName[0]).'0'; + + $this->addEntityResult($class->name, $alias); + + if ($classMetadata->discriminatorColumn) { + $discriminatorColumn = $classMetadata->discriminatorColumn; + $this->setDiscriminatorColumn($alias, $discriminatorColumn['name']); + $this->addMetaResult($alias, $discriminatorColumn['name'], $discriminatorColumn['fieldName']); + } + + foreach ($classMetadata->getColumnNames() as $key => $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName)); + } + } + } + + return $this; + } + + /** + * Adds the result set mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultSetMappingName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) + { + $counter = 0; + $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); + $rooShortName = $class->reflClass->getShortName(); + $rootAlias = strtolower($rooShortName[0]) . $counter; + + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityMapping) { + $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); + + if ($class->reflClass->name == $classMetadata->reflClass->name) { + $this->addEntityResult($classMetadata->name, $rootAlias); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); + } else { + $shortName = $classMetadata->reflClass->getShortName(); + $joinAlias = strtolower($shortName[0]) . ++ $counter; + $associations = $class->getAssociationsByTargetClass($classMetadata->name); + + foreach ($associations as $relation => $mapping) { + $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); + } + } + + } + } + + if (isset($resultMapping['columns'])) { + foreach ($resultMapping['columns'] as $entityMapping) { + $this->addScalarResult($entityMapping['name'], $entityMapping['name']); + } + } + + return $this; + } + + /** + * Adds the entity result mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $classMetadata + * @param array $entityMapping + * @param string $alias + * + * @return ResultSetMappingBuilder + * + * @throws \InvalidArgumentException + */ + public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias) + { + if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) { + $discriminatorColumn = $entityMapping['discriminatorColumn']; + $this->setDiscriminatorColumn($alias, $discriminatorColumn); + $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn); + } + + if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) { + foreach ($entityMapping['fields'] as $field) { + $fieldName = $field['name']; + $relation = null; + + if(strpos($fieldName, '.')){ + list($relation, $fieldName) = explode('.', $fieldName); + } + + if (isset($classMetadata->associationMappings[$relation])) { + if($relation) { + $associationMapping = $classMetadata->associationMappings[$relation]; + $joinAlias = $alias.$relation; + $parentAlias = $alias; + + $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation); + $this->addFieldResult($joinAlias, $field['column'], $fieldName); + }else { + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } else { + if(!isset($classMetadata->fieldMappings[$fieldName])) { + throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. "); + } + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } + + } else { + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + } + + return $this; + } + + /** + * Generates the Select clause from this ResultSetMappingBuilder. + * + * Works only for all the entity results. The select parts for scalar + * expressions have to be written manually. + * + * @param array $tableAliases + * + * @return string + */ + public function generateSelectClause($tableAliases = array()) + { + $sql = ""; + + foreach ($this->columnOwnerMap as $columnName => $dqlAlias) { + $tableAlias = isset($tableAliases[$dqlAlias]) + ? $tableAliases[$dqlAlias] : $dqlAlias; + + if ($sql) { + $sql .= ", "; + } + + $sql .= $tableAlias . "."; + + if (isset($this->fieldMappings[$columnName])) { + $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); + $sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']; + } else if (isset($this->metaMappings[$columnName])) { + $sql .= $this->metaMappings[$columnName]; + } else if (isset($this->discriminatorColumn[$columnName])) { + $sql .= $this->discriminatorColumn[$columnName]; + } + + $sql .= " AS " . $columnName; + } + + return $sql; + } + + /** + * @return string + */ + public function __toString() + { + return $this->generateSelectClause(array()); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php new file mode 100644 index 00000000..77b8ec1d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php @@ -0,0 +1,2191 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs + * the corresponding SQL. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @author Fabio B. Silva + * @since 2.0 + * @todo Rename: SQLWalker + */ +class SqlWalker implements TreeWalker +{ + /** + * @var string + */ + const HINT_DISTINCT = 'doctrine.distinct'; + + /** + * @var ResultSetMapping + */ + private $rsm; + + /** + * Counter for generating unique column aliases. + * + * @var integer + */ + private $aliasCounter = 0; + + /** + * Counter for generating unique table aliases. + * + * @var integer + */ + private $tableAliasCounter = 0; + + /** + * Counter for generating unique scalar result. + * + * @var integer + */ + private $scalarResultCounter = 1; + + /** + * Counter for generating unique parameter indexes. + * + * @var integer + */ + private $sqlParamIndex = 0; + + /** + * Counter for generating indexes. + * + * @var integer + */ + private $newObjectCounter = 0; + + /** + * @var ParserResult + */ + private $parserResult; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var \Doctrine\ORM\AbstractQuery + */ + private $query; + + /** + * @var array + */ + private $tableAliasMap = array(); + + /** + * Map from result variable names to their SQL column alias names. + * + * @var array + */ + private $scalarResultAliasMap = array(); + + /** + * Map from DQL-Alias + Field-Name to SQL Column Alias. + * + * @var array + */ + private $scalarFields = array(); + + /** + * Map of all components/classes that appear in the DQL query. + * + * @var array + */ + private $queryComponents; + + /** + * A list of classes that appear in non-scalar SelectExpressions. + * + * @var array + */ + private $selectedClasses = array(); + + /** + * The DQL alias of the root class of the currently traversed query. + * + * @var array + */ + private $rootAliases = array(); + + /** + * Flag that indicates whether to generate SQL table aliases in the SQL. + * These should only be generated for SELECT queries, not for UPDATE/DELETE. + * + * @var boolean + */ + private $useSqlTableAliases = true; + + /** + * The database platform abstraction. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * {@inheritDoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->query = $query; + $this->parserResult = $parserResult; + $this->queryComponents = $queryComponents; + $this->rsm = $parserResult->getResultSetMapping(); + $this->em = $query->getEntityManager(); + $this->conn = $this->em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Gets the Query instance used by the walker. + * + * @return Query. + */ + public function getQuery() + { + return $this->query; + } + + /** + * Gets the Connection used by the walker. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the EntityManager used by the walker. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Gets the information about a single query component. + * + * @param string $dqlAlias The DQL alias. + * + * @return array + */ + public function getQueryComponent($dqlAlias) + { + return $this->queryComponents[$dqlAlias]; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + switch (true) { + case ($AST instanceof AST\DeleteStatement): + $primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableDeleteExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + case ($AST instanceof AST\UpdateStatement): + $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableUpdateExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + default: + return new Exec\SingleSelectExecutor($AST, $this); + } + } + + /** + * Generates a unique, short SQL table alias. + * + * @param string $tableName Table name + * @param string $dqlAlias The DQL alias. + * + * @return string Generated table alias. + */ + public function getSQLTableAlias($tableName, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + if ( ! isset($this->tableAliasMap[$tableName])) { + $this->tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->tableAliasCounter++ . '_'; + } + + return $this->tableAliasMap[$tableName]; + } + + /** + * Forces the SqlWalker to use a specific alias for a table name, rather than + * generating an alias on its own. + * + * @param string $tableName + * @param string $alias + * @param string $dqlAlias + * + * @return string + */ + public function setSQLTableAlias($tableName, $alias, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + $this->tableAliasMap[$tableName] = $alias; + + return $alias; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * + * @return string + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform); + } + + /** + * Generates the SQL JOINs that are necessary for Class Table Inheritance + * for the given class. + * + * @param ClassMetadata $class The class for which to generate the joins. + * @param string $dqlAlias The DQL alias of the class. + * + * @return string The SQL. + */ + private function _generateClassTableInheritanceJoins($class, $dqlAlias) + { + $sql = ''; + + $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // INNER JOIN parent class tables + foreach ($class->parentClasses as $parentClassName) { + $parentClass = $this->em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias); + + // If this is a joined association we must use left joins to preserve the correct result. + $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER '; + $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + // Add filters on the root class + if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) { + $sqlParts[] = $filterSql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + // Ignore subclassing inclusion if partial objects is disallowed + if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + return $sql; + } + + // LEFT JOIN child class tables + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql; + } + + /** + * @return string + */ + private function _generateOrderedCollectionOrderByItems() + { + $sqlParts = array(); + + foreach ($this->selectedClasses as $selectedClass) { + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->queryComponents[$dqlAlias]; + + if ( ! isset($qComp['relation']['orderBy'])) continue; + + foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { + $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); + + $sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation; + } + } + + return implode(', ', $sqlParts); + } + + /** + * Generates a discriminator column SQL condition for the class with the given DQL alias. + * + * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions. + * + * @return string + */ + private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases) + { + $sqlParts = array(); + + foreach ($dqlAliases as $dqlAlias) { + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ( ! $class->isInheritanceTypeSingleTable()) continue; + + $conn = $this->em->getConnection(); + $values = array(); + + if ($class->discriminatorValue !== null) { // discriminators can be 0 + $values[] = $conn->quote($class->discriminatorValue); + } + + foreach ($class->subClasses as $subclassName) { + $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue); + } + + $sqlParts[] = (($this->useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '') + . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; + } + + $sql = implode(' AND ', $sqlParts); + + return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql; + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + if (!$this->em->hasFilters()) { + return ''; + } + + switch($targetEntity->inheritanceType) { + case ClassMetadata::INHERITANCE_TYPE_NONE: + break; + case ClassMetadata::INHERITANCE_TYPE_JOINED: + // The classes in the inheritance will be added to the query one by one, + // but only the root node is getting filtered + if ($targetEntity->name !== $targetEntity->rootEntityName) { + return ''; + } + break; + case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE: + // With STI the table will only be queried once, make sure that the filters + // are added to the root entity + $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); + break; + default: + //@todo: throw exception? + return ''; + break; + } + + $filterClauses = array(); + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + return implode(' AND ', $filterClauses); + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $sql = $this->walkSelectClause($AST->selectClause); + $sql .= $this->walkFromClause($AST->fromClause); + $sql .= $this->walkWhereClause($AST->whereClause); + $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; + $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : ''; + + if (($orderByClause = $AST->orderByClause) !== null) { + $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; + } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $sql .= ' ORDER BY ' . $orderBySql; + } + + $sql = $this->platform->modifyLimitQuery( + $sql, $this->query->getMaxResults(), $this->query->getFirstResult() + ); + + if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) { + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $sql .= ' ' . $this->platform->getReadLockSQL(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $sql .= ' ' . $this->platform->getWriteLockSQL(); + break; + + case LockMode::OPTIMISTIC: + foreach ($this->selectedClasses as $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name); + } + } + break; + case LockMode::NONE: + break; + + default: + throw \Doctrine\ORM\Query\QueryException::invalidLockMode(); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + $this->useSqlTableAliases = false; + + return $this->walkUpdateClause($AST->updateClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + $this->useSqlTableAliases = false; + + return $this->walkDeleteClause($AST->deleteClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL. + * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers. + * + * @param string $identVariable + * + * @return string + */ + public function walkEntityIdentificationVariable($identVariable) + { + $class = $this->queryComponents[$identVariable]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable); + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) { + $sqlParts[] = $tableAlias . '.' . $columnName; + } + + return implode(', ', $sqlParts); + } + + /** + * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL. + * + * @param string $identificationVariable + * @param string $fieldName + * + * @return string The SQL. + */ + public function walkIdentificationVariable($identificationVariable, $fieldName = null) + { + $class = $this->queryComponents[$identificationVariable]['metadata']; + + if ( + $fieldName !== null && $class->isInheritanceTypeJoined() && + isset($class->fieldMappings[$fieldName]['inherited']) + ) { + $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); + } + + return $this->getSQLTableAlias($class->getTableName(), $identificationVariable); + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + $sql = ''; + + switch ($pathExpr->type) { + case AST\PathExpression::TYPE_STATE_FIELD: + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ($this->useSqlTableAliases) { + $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.'; + } + + $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + break; + + case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION: + // 1- the owning side: + // Just use the foreign key, i.e. u.group_id + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if (isset($class->associationMappings[$fieldName]['inherited'])) { + $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ( ! $assoc['isOwningSide']) { + throw QueryException::associationPathInverseSideNotSupported(); + } + + // COMPOSITE KEYS NOT (YET?) SUPPORTED + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw QueryException::associationPathCompositeKeyNotSupported(); + } + + if ($this->useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'; + } + + $sql .= reset($assoc['targetToSourceKeyColumns']); + break; + + default: + throw QueryException::invalidPathExpression($pathExpr); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : ''); + $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)); + + if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) { + $this->query->setHint(self::HINT_DISTINCT, true); + } + + $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && + $this->query->getHydrationMode() == Query::HYDRATE_OBJECT + || + $this->query->getHydrationMode() != Query::HYDRATE_OBJECT && + $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS); + + foreach ($this->selectedClasses as $selectedClass) { + $class = $selectedClass['class']; + $dqlAlias = $selectedClass['dqlAlias']; + $resultAlias = $selectedClass['resultAlias']; + + // Register as entity or joined entity result + if ($this->queryComponents[$dqlAlias]['relation'] === null) { + $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias); + } else { + $this->rsm->addJoinedEntityResult( + $class->name, + $dqlAlias, + $this->queryComponents[$dqlAlias]['parent'], + $this->queryComponents[$dqlAlias]['relation']['fieldName'] + ); + } + + if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + // Add discriminator columns to SQL + $rootClass = $this->em->getClassMetadata($class->rootEntityName); + $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); + $discrColumn = $rootClass->discriminatorColumn; + $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); + + $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; + + $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']); + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) { + continue; + } + + // Add foreign key columns of class and also parent classes + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } else if ( !$addMetaColumns && !isset($assoc['id'])) { + continue; + } + + $owningClass = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class; + $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true)); + } + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns) { + continue; + } + + // Add foreign key columns of subclasses + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->associationMappings as $assoc) { + // Skip if association is inherited + if (isset($assoc['inherited'])) continue; + + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue; + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); + } + } + } + } + + $sql .= implode(', ', $sqlSelectExpressions); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + $identificationVarDecls = $fromClause->identificationVariableDeclarations; + $sqlParts = array(); + + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sql = $this->platform->appendLockHint( + $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + foreach ($identificationVariableDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + if ($identificationVariableDecl->indexBy) { + $alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable; + $field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field; + + if (isset($this->scalarFields[$alias][$field])) { + $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]); + } else { + $this->rsm->addIndexBy( + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field + ); + } + } + + $sqlParts[] = $sql; + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\RangeVariableDeclaration $rangeVariableDeclaration + * + * @return string + */ + public function walkRangeVariableDeclaration($rangeVariableDeclaration) + { + $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); + $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; + + $this->rootAliases[] = $dqlAlias; + + $sql = $class->getQuotedTableName($this->platform) . ' ' + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + return $sql; + } + + /** + * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration + * @param int $joinType + * + * @return string + * + * @throws QueryException + */ + public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER) + { + $sql = ''; + + $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression; + $joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable; + $indexBy = $joinAssociationDeclaration->indexBy; + + $relation = $this->queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']); + $targetTableName = $targetClass->getQuotedTableName($this->platform); + + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); + $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable); + + // Ensure we got the owning side, since it has all mapping info + $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; + + if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) { + if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) { + throw QueryException::iterateWithFetchJoinNotAllowed($assoc); + } + } + + // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot + // be the owning side and previously we ensured that $assoc is always the owning side of the associations. + // The owning side is necessary at this point because only it contains the JoinColumn information. + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + $conditions = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + if ($relation['isOwningSide']) { + $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; + + continue; + } + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions); + break; + + case ($assoc['type'] == ClassMetadata::MANY_TO_MANY): + // Join relation table + $joinTable = $assoc['joinTable']; + $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->platform); + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['joinColumns'] + : $assoc['joinTable']['inverseJoinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions); + + // Join target table + $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['inverseJoinColumns'] + : $assoc['joinTable']['joinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions); + break; + } + + // FIXME: these should either be nested or all forced to be left joins (DDC-XXX) + if ($targetClass->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); + } + + // Apply the indexes + if ($indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->rsm->addIndexBy( + $indexBy->simpleStateFieldPathExpression->identificationVariable, + $indexBy->simpleStateFieldPathExpression->field + ); + } else if (isset($relation['indexBy'])) { + $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + return $function->getSql($this); + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + $orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems); + + if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems); + } + + return ' ORDER BY ' . implode(', ', $orderByItems); + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + $expr = $orderByItem->expression; + $sql = ($expr instanceof AST\Node) + ? $expr->dispatch($this) + : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']); + + return $sql . ' ' . strtoupper($orderByItem->type); + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + $joinType = $join->joinType; + $joinDeclaration = $join->joinAssociationDeclaration; + + $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' LEFT JOIN ' + : ' INNER JOIN '; + + switch (true) { + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration): + $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName); + $condExprConjunction = $class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER + ? ' AND ' + : ' ON '; + + $sql .= $this->walkRangeVariableDeclaration($joinDeclaration) + . $condExprConjunction . '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + break; + + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): + $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType); + + // Handle WITH clause + if (($condExpr = $join->conditionalExpression) !== null) { + // Phase 2 AST optimization: Skip processing of ConditionalExpression + // if only one ConditionalTerm is defined + $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; + } + break; + } + + return $sql; + } + + /** + * Walks down a CaseExpression AST node and generates the corresponding SQL. + * + * @param AST\CoalesceExpression|AST\NullIfExpression|AST\GeneralCaseExpression|AST\SimpleCaseExpression $expression + * + * @return string The SQL. + */ + public function walkCaseExpression($expression) + { + switch (true) { + case ($expression instanceof AST\CoalesceExpression): + return $this->walkCoalesceExpression($expression); + + case ($expression instanceof AST\NullIfExpression): + return $this->walkNullIfExpression($expression); + + case ($expression instanceof AST\GeneralCaseExpression): + return $this->walkGeneralCaseExpression($expression); + + case ($expression instanceof AST\SimpleCaseExpression): + return $this->walkSimpleCaseExpression($expression); + + default: + return ''; + } + } + + /** + * Walks down a CoalesceExpression AST node and generates the corresponding SQL. + * + * @param AST\CoalesceExpression $coalesceExpression + * + * @return string The SQL. + */ + public function walkCoalesceExpression($coalesceExpression) + { + $sql = 'COALESCE('; + + $scalarExpressions = array(); + + foreach ($coalesceExpression->scalarExpressions as $scalarExpression) { + $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression); + } + + $sql .= implode(', ', $scalarExpressions) . ')'; + + return $sql; + } + + /** + * Walks down a NullIfExpression AST node and generates the corresponding SQL. + * + * @param AST\NullIfExpression $nullIfExpression + * + * @return string The SQL. + */ + public function walkNullIfExpression($nullIfExpression) + { + $firstExpression = is_string($nullIfExpression->firstExpression) + ? $this->conn->quote($nullIfExpression->firstExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression); + + $secondExpression = is_string($nullIfExpression->secondExpression) + ? $this->conn->quote($nullIfExpression->secondExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression); + + return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')'; + } + + /** + * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL. + * + * @param AST\GeneralCaseExpression $generalCaseExpression + * + * @return string The SQL. + */ + public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression) + { + $sql = 'CASE'; + + foreach ($generalCaseExpression->whenClauses as $whenClause) { + $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL. + * + * @param AST\SimpleCaseExpression $simpleCaseExpression + * + * @return string The SQL. + */ + public function walkSimpleCaseExpression($simpleCaseExpression) + { + $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand); + + foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) { + $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + $sql = ''; + $expr = $selectExpression->expression; + $hidden = $selectExpression->hiddenAliasResultVariable; + + switch (true) { + case ($expr instanceof AST\PathExpression): + if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) { + throw QueryException::invalidPathExpression($expr); + } + + $fieldName = $expr->field; + $dqlAlias = $expr->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName; + $tableName = ($class->isInheritanceTypeJoined()) + ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName) + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + $columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']); + + $col = $sqlTableAlias . '.' . $columnName; + + $fieldType = $class->getTypeOfField($fieldName); + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($fieldType); + $col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform()); + } + + $sql .= $col . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias; + } + break; + + case ($expr instanceof AST\AggregateExpression): + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\ParenthesisExpression): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + case ($expr instanceof AST\Subselect): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + case ($expr instanceof AST\NewObjectExpression): + $sql .= $this->walkNewObject($expr); + break; + + default: + // IdentificationVariable or PartialObjectExpression + if ($expr instanceof AST\PartialObjectExpression) { + $dqlAlias = $expr->identificationVariable; + $partialFieldSet = $expr->partialFieldSet; + } else { + $dqlAlias = $expr; + $partialFieldSet = array(); + } + + $queryComp = $this->queryComponents[$dqlAlias]; + $class = $queryComp['metadata']; + $resultAlias = $selectExpression->fieldIdentificationVariable ?: null; + + if ( ! isset($this->selectedClasses[$dqlAlias])) { + $this->selectedClasses[$dqlAlias] = array( + 'class' => $class, + 'dqlAlias' => $dqlAlias, + 'resultAlias' => $resultAlias + ); + } + + $sqlParts = array(); + + // Select all fields from the queried class + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) { + continue; + } + + $tableName = (isset($mapping['inherited'])) + ? $this->em->getClassMetadata($mapping['inherited'])->getTableName() + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->platform); + } + + $sqlParts[] = $col . ' AS '. $columnAlias; + + $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); + } + + // Add any additional fields of subclasses (excluding inherited fields) + // 1) on Single Table Inheritance: always, since its marginal overhead + // 2) on Class Table Inheritance only if partial objects are disallowed, + // since it requires outer joining subtables. + if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { + continue; + } + + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($subClass->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($subClass->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->platform); + } + + $sqlParts[] = $col . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); + } + } + } + + $sql .= implode(', ', $sqlParts); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + $useAliasesBefore = $this->useSqlTableAliases; + $rootAliasesBefore = $this->rootAliases; + + $this->rootAliases = array(); // reset the rootAliases for the subselect + $this->useSqlTableAliases = true; + + $sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause); + $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause); + $sql .= $this->walkWhereClause($subselect->whereClause); + + $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : ''; + $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : ''; + $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : ''; + + $this->rootAliases = $rootAliasesBefore; // put the main aliases back + $this->useSqlTableAliases = $useAliasesBefore; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; + $sqlParts = array (); + + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sql = $this->platform->appendLockHint( + $this->walkRangeVariableDeclaration($subselectIdVarDecl->rangeVariableDeclaration), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + foreach ($subselectIdVarDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + $sqlParts[] = $sql; + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') + . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); + } + + /** + * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression + * + * @return string. + */ + public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression) + { + return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this)); + } + + /** + * @param AST\NewObjectExpression $newObjectExpression + * + * @return string The SQL. + */ + public function walkNewObject($newObjectExpression) + { + $sqlSelectExpressions = array(); + $objIndex = $this->newObjectCounter++; + + foreach ($newObjectExpression->args as $argIndex => $e) { + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr'); + + switch (true) { + case ($e instanceof AST\NewObjectExpression): + $sqlSelectExpressions[] = $e->dispatch($this); + break; + + default: + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + } + + switch (true) { + case ($e instanceof AST\PathExpression): + $fieldName = $e->field; + $dqlAlias = $e->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + $fieldType = $class->getTypeOfField($fieldName); + break; + + case ($e instanceof AST\Literal): + switch ($e->type) { + case AST\Literal::BOOLEAN: + $fieldType = 'boolean'; + break; + + case AST\Literal::NUMERIC: + $fieldType = is_float($e->value) ? 'float' : 'integer'; + break; + } + break; + + default: + $fieldType = 'string'; + break; + } + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $newObjectExpression->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); + } + + return implode(', ', $sqlSelectExpressions); + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + $expr = $simpleSelectExpression->expression; + $sql = ' '; + + switch (true) { + case ($expr instanceof AST\PathExpression): + $sql .= $this->walkPathExpression($expr); + break; + + case ($expr instanceof AST\AggregateExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; + break; + + case ($expr instanceof AST\Subselect): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $columnAlias = 'sclr' . $this->aliasCounter++; + $this->scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + break; + + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $columnAlias = $this->getSQLColumnAlias('sclr'); + $this->scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + break; + + default: // IdentificationVariable + $sql .= $this->walkEntityIdentificationVariable($expr); + break; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') + . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + $sqlParts = array(); + + foreach ($groupByClause->groupByItems as $groupByItem) { + $sqlParts[] = $this->walkGroupByItem($groupByItem); + } + + return ' GROUP BY ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + // StateFieldPathExpression + if ( ! is_string($groupByItem)) { + return $this->walkPathExpression($groupByItem); + } + + // ResultVariable + if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { + return $this->walkResultVariable($groupByItem); + } + + // IdentificationVariable + $sqlParts = array(); + + foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field); + $item->type = AST\PathExpression::TYPE_STATE_FIELD; + + $sqlParts[] = $this->walkPathExpression($item); + } + + foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) { + if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']); + $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + + $sqlParts[] = $this->walkPathExpression($item); + } + } + + return implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable); + $this->rootAliases[] = $deleteClause->aliasIdentificationVariable; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + $class = $this->em->getClassMetadata($updateClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable); + $this->rootAliases[] = $updateClause->aliasIdentificationVariable; + + $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + $useTableAliasesBefore = $this->useSqlTableAliases; + $this->useSqlTableAliases = false; + + $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = '; + $newValue = $updateItem->newValue; + + switch (true) { + case ($newValue instanceof AST\Node): + $sql .= $newValue->dispatch($this); + break; + + case ($newValue === null): + $sql .= 'NULL'; + break; + + default: + $sql .= $this->conn->quote($newValue); + break; + } + + $this->useSqlTableAliases = $useTableAliasesBefore; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + $condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : ''; + $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases); + + if ($this->em->hasFilters()) { + $filterClauses = array(); + foreach ($this->rootAliases as $dqlAlias) { + $class = $this->queryComponents[$dqlAlias]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + + if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) { + $filterClauses[] = $filterExpr; + } + } + + if (count($filterClauses)) { + if ($condSql) { + $condSql = '(' . $condSql . ') AND '; + } + + $condSql .= implode(' AND ', $filterClauses); + } + } + + if ($condSql) { + return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); + } + + if ($discrSql) { + return ' WHERE ' . $discrSql; + } + + return ''; + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + // Phase 2 AST optimization: Skip processing of ConditionalExpression + // if only one ConditionalTerm is defined + if ( ! ($condExpr instanceof AST\ConditionalExpression)) { + return $this->walkConditionalTerm($condExpr); + } + + return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + // Phase 2 AST optimization: Skip processing of ConditionalTerm + // if only one ConditionalFactor is defined + if ( ! ($condTerm instanceof AST\ConditionalTerm)) { + return $this->walkConditionalFactor($condTerm); + } + + return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + // Phase 2 AST optimization: Skip processing of ConditionalFactor + // if only one ConditionalPrimary is defined + return ( ! ($factor instanceof AST\ConditionalFactor)) + ? $this->walkConditionalPrimary($factor) + : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + if ($primary->isSimpleConditionalExpression()) { + return $primary->simpleConditionalExpression->dispatch($this); + } + + if ($primary->isConditionalExpression()) { + $condExpr = $primary->conditionalExpression; + + return '(' . $this->walkConditionalExpression($condExpr) . ')'; + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + $sql = ($existsExpr->not) ? 'NOT ' : ''; + + $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + $sql = $collMemberExpr->not ? 'NOT ' : ''; + $sql .= 'EXISTS (SELECT 1 FROM '; + + $entityExpr = $collMemberExpr->entityExpression; + $collPathExpr = $collMemberExpr->collectionValuedPathExpression; + + $fieldName = $collPathExpr->field; + $dqlAlias = $collPathExpr->identificationVariable; + + $class = $this->queryComponents[$dqlAlias]['metadata']; + + switch (true) { + // InputParameter + case ($entityExpr instanceof AST\InputParameter): + $dqlParamKey = $entityExpr->name; + $entitySql = '?'; + break; + + // SingleValuedAssociationPathExpression | IdentificationVariable + case ($entityExpr instanceof AST\PathExpression): + $entitySql = $this->walkPathExpression($entityExpr); + break; + + default: + throw new \BadMethodCallException("Not implemented"); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + $sqlParts = array(); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform); + + $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } else { // many-to-many + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias + . ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON '; + + // join conditions + $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; + $joinSqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $this->platform); + + $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn; + } + + $sql .= implode(' AND ', $joinSqlParts); + $sql .= ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; + $sqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform); + + $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + $sizeFunc = new AST\Functions\SizeFunction('size'); + $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; + + return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0'); + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + $expression = $nullCompExpr->expression; + $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + + if ($expression instanceof AST\InputParameter) { + $this->parserResult->addParameterMapping($expression->name, $this->sqlParamIndex++); + + return '?' . $comparison; + } + + return $expression->dispatch($this) . $comparison; + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN ('; + + $sql .= ($inExpr->subselect) + ? $this->walkSubselect($inExpr->subselect) + : implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals)); + + $sql .= ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInstanceOfExpression($instanceOfExpr) + { + $sql = ''; + + $dqlAlias = $instanceOfExpr->identificationVariable; + $discrClass = $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ($class->discriminatorColumn) { + $discrClass = $this->em->getClassMetadata($class->rootEntityName); + } + + if ($this->useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.'; + } + + $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); + + $sqlParameterList = array(); + + foreach ($instanceOfExpr->value as $parameter) { + if ($parameter instanceof AST\InputParameter) { + $sqlParameterList[] = $this->walkInputParameter($parameter); + } else { + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->em->getClassMetadata($parameter)->name; + + if ($entityClassName == $class->name) { + $sqlParameterList[] = $this->conn->quote($class->discriminatorValue); + } else { + $discrMap = array_flip($class->discriminatorMap); + + if (!isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $sqlParameterList[] = $this->conn->quote($discrMap[$entityClassName]); + } + } + } + + $sql .= '(' . implode(', ', $sqlParameterList) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInParameter($inParam) + { + return $inParam instanceof AST\InputParameter + ? $this->walkInputParameter($inParam) + : $this->walkLiteral($inParam); + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + switch ($literal->type) { + case AST\Literal::STRING: + return $this->conn->quote($literal->value); + + case AST\Literal::BOOLEAN: + $bool = strtolower($literal->value) == 'true' ? true : false; + $boolVal = $this->conn->getDatabasePlatform()->convertBooleans($bool); + + return $boolVal; + + case AST\Literal::NUMERIC: + return $literal->value; + + default: + throw QueryException::invalidLiteral($literal); + } + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + $sql = $this->walkArithmeticExpression($betweenExpr->expression); + + if ($betweenExpr->not) $sql .= ' NOT'; + + $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression) + . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + $stringExpr = $likeExpr->stringExpression; + $sql = $stringExpr->dispatch($this) . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + + if ($likeExpr->stringPattern instanceof AST\InputParameter) { + $inputParam = $likeExpr->stringPattern; + $dqlParamKey = $inputParam->name; + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + $sql .= '?'; + } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode ) { + $sql .= $this->walkFunction($likeExpr->stringPattern); + } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) { + $sql .= $this->walkPathExpression($likeExpr->stringPattern); + } else { + $sql .= $this->walkLiteral($likeExpr->stringPattern); + } + + if ($likeExpr->escapeChar) { + $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + return $this->walkPathExpression($stateFieldPathExpression); + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + $leftExpr = $compExpr->leftExpression; + $rightExpr = $compExpr->rightExpression; + $sql = ''; + + $sql .= ($leftExpr instanceof AST\Node) + ? $leftExpr->dispatch($this) + : (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr)); + + $sql .= ' ' . $compExpr->operator . ' '; + + $sql .= ($rightExpr instanceof AST\Node) + ? $rightExpr->dispatch($this) + : (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++); + + return '?'; + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + return ($arithmeticExpr->isSimpleArithmeticExpression()) + ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) + : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { + return $this->walkArithmeticTerm($simpleArithmeticExpr); + } + + return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)); + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + if (is_string($term)) { + return (isset($this->queryComponents[$term])) + ? $this->walkResultVariable($this->queryComponents[$term]['token']['value']) + : $term; + } + + // Phase 2 AST optimization: Skip processing of ArithmeticTerm + // if only one ArithmeticFactor is defined + if ( ! ($term instanceof AST\ArithmeticTerm)) { + return $this->walkArithmeticFactor($term); + } + + return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)); + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + if (is_string($factor)) { + return $factor; + } + + // Phase 2 AST optimization: Skip processing of ArithmeticFactor + // if only one ArithmeticPrimary is defined + if ( ! ($factor instanceof AST\ArithmeticFactor)) { + return $this->walkArithmeticPrimary($factor); + } + + $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''); + + return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary); + } + + /** + * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $primary + * + * @return string The SQL. + */ + public function walkArithmeticPrimary($primary) + { + if ($primary instanceof AST\SimpleArithmeticExpression) { + return '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; + } + + if ($primary instanceof AST\Node) { + return $primary->dispatch($this); + } + + return $this->walkEntityIdentificationVariable($primary); + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + return (is_string($stringPrimary)) + ? $this->conn->quote($stringPrimary) + : $stringPrimary->dispatch($this); + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + $resultAlias = $this->scalarResultAliasMap[$resultVariable]; + + if (is_array($resultAlias)) { + return implode(', ', $resultAlias); + } + + return $resultAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php new file mode 100644 index 00000000..9ddd86b0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php @@ -0,0 +1,479 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Interface for walkers of DQL ASTs (abstract syntax trees). + * + * @author Roman Borschel + * @since 2.0 + */ +interface TreeWalker +{ + /** + * Initializes TreeWalker with important information about the ASTs to be walked. + * + * @param \Doctrine\ORM\AbstractQuery $query The parsed Query. + * @param \Doctrine\ORM\Query\ParserResult $parserResult The result of the parsing process. + * @param array $queryComponents The query components (symbol table). + */ + public function __construct($query, $parserResult, array $queryComponents); + + /** + * Returns internal queryComponents array. + * + * @return array + */ + public function getQueryComponents(); + + /** + * Sets or overrides a query component for a given dql alias. + * + * @param string $dqlAlias The DQL alias. + * @param array $queryComponent + * + * @return void + */ + public function setQueryComponent($dqlAlias, array $queryComponent); + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectStatement $AST + * + * @return string The SQL. + */ + function walkSelectStatement(AST\SelectStatement $AST); + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectClause $selectClause + * + * @return string The SQL. + */ + function walkSelectClause($selectClause); + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\FromClause $fromClause + * + * @return string The SQL. + */ + function walkFromClause($fromClause); + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @param AST\Functions\FunctionNode $function + * + * @return string The SQL. + */ + function walkFunction($function); + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByClause $orderByClause + * + * @return string The SQL. + */ + function walkOrderByClause($orderByClause); + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByItem $orderByItem + * + * @return string The SQL. + */ + function walkOrderByItem($orderByItem); + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param AST\HavingClause $havingClause + * + * @return string The SQL. + */ + function walkHavingClause($havingClause); + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param AST\Join $join + * + * @return string The SQL. + */ + function walkJoin($join); + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param AST\SelectExpression $selectExpression + * + * @return string The SQL. + */ + function walkSelectExpression($selectExpression); + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\QuantifiedExpression $qExpr + * + * @return string The SQL. + */ + function walkQuantifiedExpression($qExpr); + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param AST\Subselect $subselect + * + * @return string The SQL. + */ + function walkSubselect($subselect); + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SubselectFromClause $subselectFromClause + * + * @return string The SQL. + */ + function walkSubselectFromClause($subselectFromClause); + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectClause $simpleSelectClause + * + * @return string The SQL. + */ + function walkSimpleSelectClause($simpleSelectClause); + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectExpression $simpleSelectExpression + * + * @return string The SQL. + */ + function walkSimpleSelectExpression($simpleSelectExpression); + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\AggregateExpression $aggExpression + * + * @return string The SQL. + */ + function walkAggregateExpression($aggExpression); + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\GroupByClause $groupByClause + * + * @return string The SQL. + */ + function walkGroupByClause($groupByClause); + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression|string $groupByItem + * + * @return string The SQL. + */ + function walkGroupByItem($groupByItem); + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateStatement $AST + * + * @return string The SQL. + */ + function walkUpdateStatement(AST\UpdateStatement $AST); + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteStatement $AST + * + * @return string The SQL. + */ + function walkDeleteStatement(AST\DeleteStatement $AST); + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteClause $deleteClause + * + * @return string The SQL. + */ + function walkDeleteClause(AST\DeleteClause $deleteClause); + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateClause $updateClause + * + * @return string The SQL. + */ + function walkUpdateClause($updateClause); + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateItem $updateItem + * + * @return string The SQL. + */ + function walkUpdateItem($updateItem); + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * WhereClause or not, the appropriate discriminator sql is added. + * + * @param AST\WhereClause $whereClause + * + * @return string The SQL. + */ + function walkWhereClause($whereClause); + + /** + * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalExpression $condExpr + * + * @return string The SQL. + */ + function walkConditionalExpression($condExpr); + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalTerm $condTerm + * + * @return string The SQL. + */ + function walkConditionalTerm($condTerm); + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalFactor $factor + * + * @return string The SQL. + */ + function walkConditionalFactor($factor); + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalPrimary $primary + * + * @return string The SQL. + */ + function walkConditionalPrimary($primary); + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ExistsExpression $existsExpr + * + * @return string The SQL. + */ + function walkExistsExpression($existsExpr); + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\CollectionMemberExpression $collMemberExpr + * + * @return string The SQL. + */ + function walkCollectionMemberExpression($collMemberExpr); + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\EmptyCollectionComparisonExpression $emptyCollCompExpr + * + * @return string The SQL. + */ + function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\NullComparisonExpression $nullCompExpr + * + * @return string The SQL. + */ + function walkNullComparisonExpression($nullCompExpr); + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InExpression $inExpr + * + * @return string The SQL. + */ + function walkInExpression($inExpr); + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InstanceOfExpression $instanceOfExpr + * + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr); + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $literal + * + * @return string The SQL. + */ + function walkLiteral($literal); + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\BetweenExpression $betweenExpr + * + * @return string The SQL. + */ + function walkBetweenExpression($betweenExpr); + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\LikeExpression $likeExpr + * + * @return string The SQL. + */ + function walkLikeExpression($likeExpr); + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression $stateFieldPathExpression + * + * @return string The SQL. + */ + function walkStateFieldPathExpression($stateFieldPathExpression); + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ComparisonExpression $compExpr + * + * @return string The SQL. + */ + function walkComparisonExpression($compExpr); + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param AST\InputParameter $inputParam + * + * @return string The SQL. + */ + function walkInputParameter($inputParam); + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ArithmeticExpression $arithmeticExpr + * + * @return string The SQL. + */ + function walkArithmeticExpression($arithmeticExpr); + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed $term + * + * @return string The SQL. + */ + function walkArithmeticTerm($term); + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $stringPrimary + * + * @return string The SQL. + */ + function walkStringPrimary($stringPrimary); + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $factor + * + * @return string The SQL. + */ + function walkArithmeticFactor($factor); + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleArithmeticExpression $simpleArithmeticExpr + * + * @return string The SQL. + */ + function walkSimpleArithmeticExpression($simpleArithmeticExpr); + + /** + * Walks down a PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed $pathExpr + * + * @return string The SQL. + */ + function walkPathExpression($pathExpr); + + /** + * Walks down a ResultVariable that represents an AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * + * @return string The SQL. + */ + function walkResultVariable($resultVariable); + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST + * + * @return Exec\AbstractSqlExecutor + */ + function getExecutor($AST); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php new file mode 100644 index 00000000..e95155c1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -0,0 +1,440 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * An adapter implementation of the TreeWalker interface. The methods in this class + * are empty. This class exists as convenience for creating tree walkers. + * + * @author Roman Borschel + * @since 2.0 + */ +abstract class TreeWalkerAdapter implements TreeWalker +{ + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * @return array + */ + protected function _getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * Retrieves the Query Instance responsible for the current walkers execution. + * + * @return \Doctrine\ORM\AbstractQuery + */ + protected function _getQuery() + { + return $this->_query; + } + + /** + * Retrieves the ParserResult. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + protected function _getParserResult() + { + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php new file mode 100644 index 00000000..4ce356d8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php @@ -0,0 +1,574 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Represents a chain of tree walkers that modify an AST and finally emit output. + * Only the last walker in the chain can emit output. Any previous walkers can modify + * the AST to influence the final output produced by the last walker. + * + * @author Roman Borschel + * @since 2.0 + */ +class TreeWalkerChain implements TreeWalker +{ + /** + * The tree walkers. + * + * @var TreeWalker[] + */ + private $_walkers = array(); + + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * Returns the internal queryComponents array. + * + * @return array + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * Adds a tree walker to the chain. + * + * @param string $walkerClass The class of the walker to instantiate. + * + * @return void + */ + public function addTreeWalker($walkerClass) + { + $this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents); + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectStatement($AST); + + $this->_queryComponents = $walker->getQueryComponents(); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectClause($selectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkFromClause($fromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + foreach ($this->_walkers as $walker) { + $walker->walkFunction($function); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByClause($orderByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByItem($orderByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkHavingClause($havingClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + foreach ($this->_walkers as $walker) { + $walker->walkJoin($join); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectExpression($selectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkQuantifiedExpression($qExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselect($subselect); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselectFromClause($subselectFromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectClause($simpleSelectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectExpression($simpleSelectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkAggregateExpression($aggExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByClause($groupByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByItem($groupByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteClause($deleteClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateClause($updateClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateItem($updateItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkWhereClause($whereClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalExpression($condExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalTerm($condTerm); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($condPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalPrimary($condPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkExistsExpression($existsExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkCollectionMemberExpression($collMemberExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkNullComparisonExpression($nullCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInExpression($inExpr); + } + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInstanceOfExpression($instanceOfExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + foreach ($this->_walkers as $walker) { + $walker->walkLiteral($literal); + } + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkBetweenExpression($betweenExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkLikeExpression($likeExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkStateFieldPathExpression($stateFieldPathExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkComparisonExpression($compExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + foreach ($this->_walkers as $walker) { + $walker->walkInputParameter($inputParam); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticExpression($arithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticTerm($term); + } + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkStringPrimary($stringPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkPathExpression($pathExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + foreach ($this->_walkers as $walker) { + $walker->walkResultVariable($resultVariable); + } + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php new file mode 100644 index 00000000..76d2e506 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php @@ -0,0 +1,1296 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; + +use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\QueryExpressionVisitor; + +/** + * This class is responsible for building DQL query strings via an object oriented + * PHP interface. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryBuilder +{ + /* The query types. */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + + /* The builder states. */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * The EntityManager used by this QueryBuilder. + * + * @var EntityManager + */ + private $_em; + + /** + * The array of DQL parts collected. + * + * @var array + */ + private $_dqlParts = array( + 'distinct' => false, + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $_type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * The complete DQL string for this query. + * + * @var string + */ + private $_dql; + + /** + * The query parameters. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + private $parameters = array(); + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $_maxResults = null; + + /** + * Keeps root entity alias names for join entities. + * + * @var array + */ + private $joinRootAliases = array(); + + /** + * Initializes a new QueryBuilder that uses the given EntityManager. + * + * @param EntityManager $em The EntityManager to use. + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $em->createQueryBuilder(); + * $qb + * ->select('u') + * ->from('User', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return Query\Expr + */ + public function expr() + { + return $this->_em->getExpressionBuilder(); + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->_type; + } + + /** + * Gets the associated EntityManager for this query builder. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->_state; + } + + /** + * Gets the complete DQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getDql(); // SELECT u FROM User u + * + * + * @return string The DQL query string. + */ + public function getDQL() + { + if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) { + return $this->_dql; + } + + switch ($this->_type) { + case self::DELETE: + $dql = $this->_getDQLForDelete(); + break; + + case self::UPDATE: + $dql = $this->_getDQLForUpdate(); + break; + + case self::SELECT: + default: + $dql = $this->_getDQLForSelect(); + break; + } + + $this->_state = self::STATE_CLEAN; + $this->_dql = $dql; + + return $dql; + } + + /** + * Constructs a Query instance from the current specifications of the builder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * $q = $qb->getQuery(); + * $results = $q->execute(); + * + * + * @return Query + */ + public function getQuery() + { + $parameters = clone $this->parameters; + + return $this->_em->createQuery($this->getDQL()) + ->setParameters($parameters) + ->setFirstResult($this->_firstResult) + ->setMaxResults($this->_maxResults); + } + + /** + * Finds the root entity alias of the joined entity. + * + * @param string $alias The alias of the new join entity + * @param string $parentAlias The parent entity alias of the join relationship + * + * @return string + */ + private function findRootAlias($alias, $parentAlias) + { + $rootAlias = null; + + if (in_array($parentAlias, $this->getRootAliases())) { + $rootAlias = $parentAlias; + } elseif (isset($this->joinRootAliases[$parentAlias])) { + $rootAlias = $this->joinRootAliases[$parentAlias]; + } else { + // Should never happen with correct joining order. Might be + // thoughtful to throw exception instead. + $rootAlias = $this->getRootAlias(); + } + + $this->joinRootAliases[$alias] = $rootAlias; + + return $rootAlias; + } + + /** + * Gets the FIRST root alias of the query. This is the first entity alias involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * echo $qb->getRootAlias(); // u + * + * + * @deprecated Please use $qb->getRootAliases() instead. + * + * @return string + */ + public function getRootAlias() + { + $aliases = $this->getRootAliases(); + return $aliases[0]; + } + + /** + * Gets the root aliases of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootAliases(); // array('u') + * + * + * @return array + */ + public function getRootAliases() + { + $aliases = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $aliases[] = $fromClause->getAlias(); + } + + return $aliases; + } + + /** + * Gets the root entities of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootEntities(); // array('User') + * + * + * @return array + */ + public function getRootEntities() + { + $entities = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $entities[] = $fromClause->getFrom(); + } + + return $entities; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id') + * ->setParameter('user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $parameter = new Query\Parameter($key, $value, $type); + + $this->parameters->add($parameter); + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(new ArrayCollection(array( + * new Parameter('user_id1', 1), + * new Parameter('user_id2', 2) + * ))); + * + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters to set. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameter = new Query\Parameter($key, $value); + + $parameterCollection->add($parameter); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return \Doctrine\Common\Collections\ArrayCollection The currently defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return Query\Parameter|null The value of the bound parameter. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'join', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $dqlPartName + * @param Expr\Base $dqlPart + * @param bool $append + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function add($dqlPartName, $dqlPart, $append = false) + { + if ($append && ($dqlPartName === "where" || $dqlPartName === "having")) { + throw new \InvalidArgumentException( + "Using \$append = true does not have an effect with 'where' or 'having' ". + "parts. See QueryBuilder#andWhere() for an example for correct usage." + ); + } + + $isMultiple = is_array($this->_dqlParts[$dqlPartName]); + + // This is introduced for backwards compatibility reasons. + // TODO: Remove for 3.0 + if ($dqlPartName == 'join') { + $newDqlPart = array(); + + foreach ($dqlPart as $k => $v) { + $k = is_numeric($k) ? $this->getRootAlias() : $k; + + $newDqlPart[$k] = $v; + } + + $dqlPart = $newDqlPart; + } + + if ($append && $isMultiple) { + if (is_array($dqlPart)) { + $key = key($dqlPart); + + $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; + } else { + $this->_dqlParts[$dqlPartName][] = $dqlPart; + } + } else { + $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart; + } + + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u', 'p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expressions. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), false); + } + + /** + * Adds a DISTINCT flag to this query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->distinct() + * ->from('User', 'u'); + * + * + * @param bool $flag + * + * @return QueryBuilder + */ + public function distinct($flag = true) + { + $this->_dqlParts['distinct'] = (bool) $flag; + + return $this; + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->addSelect('p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->delete('User', 'u') + * ->where('u.id = :user_id'); + * ->setParameter('user_id', 1); + * + * + * @param string $delete The class/type whose instances are subject to the deletion. + * @param string $alias The class/type alias used in the constructed query. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->_type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', new Expr\From($delete, $alias)); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The class/type whose instances are subject to the update. + * @param string $alias The class/type alias used in the constructed query. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->_type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', new Expr\From($update, $alias)); + } + + /** + * Creates and adds a query root corresponding to the entity identified by the given alias, + * forming a cartesian product with any existing query roots. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * + * + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias, $indexBy = null) + { + return $this->add('from', new Expr\From($from, $alias, $indexBy), true); + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * [php] + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $join = new Expr\Join( + Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Creates and adds a left join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $join = new Expr\Join( + Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Sets a new value for a field in a bulk update query. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The key/field to set. + * @param string $value The value, expression, placeholder, etc. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $em->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) { + $predicates = new Expr\Andx(func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * + * @return QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function andWhere($where) + { + $where = $this->getDQLPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Andx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Andx($args); + } + + return $this->add('where', $where); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement. + * + * @return QueryBuilder + * + * @see where() + */ + public function orWhere($where) + { + $where = $this->getDqlPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Orx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Orx($args); + } + + return $this->add('where', $where); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.id'); + * + * + * @param string $groupBy The grouping expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args())); + } + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param string $groupBy The grouping expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { + $having = new Expr\Andx(func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + if ($having instanceof Expr\Andx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Andx($args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + if ($having instanceof Expr\Orx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Orx($args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy); + } + + /** + * Adds an ordering to the query results. + * + * @param string|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy, true); + } + + /** + * Adds criteria to the query. + * + * Adds where expressions with AND operator. + * Adds orderings. + * Overrides firstResult and maxResults if they're set. + * + * @param Criteria $criteria + * + * @return QueryBuilder + */ + public function addCriteria(Criteria $criteria) + { + $visitor = new QueryExpressionVisitor(); + + if ($whereExpression = $criteria->getWhereExpression()) { + $this->andWhere($visitor->dispatch($whereExpression)); + foreach ($visitor->getParameters() as $parameter) { + $this->parameters->add($parameter); + } + } + + if ($criteria->getOrderings()) { + foreach ($criteria->getOrderings() as $sort => $order) { + $this->addOrderBy($sort, $order); + } + } + + // Overwrite limits only if they was set in criteria + if (($firstResult = $criteria->getFirstResult()) !== null) { + $this->setFirstResult($firstResult); + } + if (($maxResults = $criteria->getMaxResults()) !== null) { + $this->setMaxResults($maxResults); + } + + return $this; + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed $queryPart + * + * @todo Rename: getQueryPart (or remove?) + */ + public function getDQLPart($queryPartName) + { + return $this->_dqlParts[$queryPartName]; + } + + /** + * Gets all query parts. + * + * @return array $dqlParts + * + * @todo Rename: getQueryParts (or remove?) + */ + public function getDQLParts() + { + return $this->_dqlParts; + } + + /** + * @return string + */ + private function _getDQLForDelete() + { + return 'DELETE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + /** + * @return string + */ + private function _getDQLForUpdate() + { + return 'UPDATE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + /** + * @return string + */ + private function _getDQLForSelect() + { + $dql = 'SELECT' + . ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '') + . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')); + + $fromParts = $this->getDQLPart('from'); + $joinParts = $this->getDQLPart('join'); + $fromClauses = array(); + + // Loop through all FROM clauses + if ( ! empty($fromParts)) { + $dql .= ' FROM '; + + foreach ($fromParts as $from) { + $fromClause = (string) $from; + + if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) { + foreach ($joinParts[$from->getAlias()] as $join) { + $fromClause .= ' ' . ((string) $join); + } + } + + $fromClauses[] = $fromClause; + } + } + + $dql .= implode(', ', $fromClauses) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + + return $dql; + } + + /** + * @param string $queryPartName + * @param array $options + * + * @return string + */ + private function _getReducedDQLQueryPart($queryPartName, $options = array()) + { + $queryPart = $this->getDQLPart($queryPartName); + + if (empty($queryPart)) { + return (isset($options['empty']) ? $options['empty'] : ''); + } + + return (isset($options['pre']) ? $options['pre'] : '') + . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart) + . (isset($options['post']) ? $options['post'] : ''); + } + + /** + * Resets DQL parts. + * + * @param array|null $parts + * + * @return QueryBuilder + */ + public function resetDQLParts($parts = null) + { + if (is_null($parts)) { + $parts = array_keys($this->_dqlParts); + } + + foreach ($parts as $part) { + $this->resetDQLPart($part); + } + + return $this; + } + + /** + * Resets single DQL part. + * + * @param string $part + * + * @return QueryBuilder + */ + public function resetDQLPart($part) + { + $this->_dqlParts[$part] = is_array($this->_dqlParts[$part]) ? array() : null; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final DQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getDQL(); + } + + /** + * Deep clones all expression objects in the DQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->_dqlParts as $part => $elements) { + if (is_array($this->_dqlParts[$part])) { + foreach ($this->_dqlParts[$part] as $idx => $element) { + if (is_object($element)) { + $this->_dqlParts[$part][$idx] = clone $element; + } + } + } else if (is_object($elements)) { + $this->_dqlParts[$part] = clone $elements; + } + } + + $parameters = array(); + + foreach ($this->parameters as $parameter) { + $parameters[] = clone $parameter; + } + + $this->parameters = new ArrayCollection($parameters); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown b/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown new file mode 100644 index 00000000..e69de29b diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php new file mode 100644 index 00000000..c90adbed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:metadata') + ->setDescription('Clear all metadata cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the metadata cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getMetadataCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->writeln('Clearing ALL Metadata cache entries'); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->writeln($message); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php new file mode 100644 index 00000000..6c8d761c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:query') + ->setDescription('Clear all query cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the query cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getQueryCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Query cache entries' . PHP_EOL); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->write($message . PHP_EOL); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php new file mode 100644 index 00000000..26eb3d46 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ResultCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:result') + ->setDescription('Clear all result cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the result cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getResultCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->writeln('Clearing ALL Result cache entries'); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->writeln($message); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php new file mode 100644 index 00000000..71d72deb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -0,0 +1,230 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\ConvertDoctrine1Schema; +use Doctrine\ORM\Tools\EntityGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1SchemaCommand extends Command +{ + /** + * @var EntityGenerator|null + */ + private $entityGenerator = null; + + /** + * @var ClassMetadataExporter|null + */ + private $metadataExporter = null; + + /** + * @return EntityGenerator + */ + public function getEntityGenerator() + { + if ($this->entityGenerator == null) { + $this->entityGenerator = new EntityGenerator(); + } + + return $this->entityGenerator; + } + + /** + * @param EntityGenerator $entityGenerator + * + * @return void + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->entityGenerator = $entityGenerator; + } + + /** + * @return ClassMetadataExporter + */ + public function getMetadataExporter() + { + if ($this->metadataExporter == null) { + $this->metadataExporter = new ClassMetadataExporter(); + } + + return $this->metadataExporter; + } + + /** + * @param ClassMetadataExporter $metadataExporter + * + * @return void + */ + public function setMetadataExporter(ClassMetadataExporter $metadataExporter) + { + $this->metadataExporter = $metadataExporter; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-d1-schema') + ->setAliases(array('orm:convert:d1-schema')) + ->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.') + ->setDefinition(array( + new InputArgument( + 'from-path', InputArgument::REQUIRED, 'The path of Doctrine 1.X schema information.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The destination Doctrine 2.X mapping type.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your Doctrine 2.X mapping information.' + ), + new InputOption( + 'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'Optional paths of Doctrine 1.X schema information.', + array() + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<getArgument('from-path')), $input->getOption('from')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + $toType = $input->getArgument('to-type'); + $extend = $input->getOption('extend'); + $numSpaces = $input->getOption('num-spaces'); + + $this->convertDoctrine1Schema($fromPaths, $destPath, $toType, $numSpaces, $extend, $output); + } + + /** + * @param array $fromPaths + * @param string $destPath + * @param string $toType + * @param int $numSpaces + * @param string|null $extend + * @param OutputInterface $output + * + * @throws \InvalidArgumentException + */ + public function convertDoctrine1Schema(array $fromPaths, $destPath, $toType, $numSpaces, $extend, OutputInterface $output) + { + foreach ($fromPaths as &$dirName) { + $dirName = realpath($dirName); + + if ( ! file_exists($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not exist.", $dirName) + ); + } + + if ( ! is_readable($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not have read permissions.", $dirName) + ); + } + } + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not exist.", $destPath) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $cme = $this->getMetadataExporter(); + $exporter = $cme->getExporter($toType, $destPath); + + if (strtolower($toType) === 'annotation') { + $entityGenerator = $this->getEntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($numSpaces); + + if ($extend !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + $converter = new ConvertDoctrine1Schema($fromPaths); + $metadata = $converter->getMetadata(); + + if ($metadata) { + $output->writeln(''); + + foreach ($metadata as $class) { + $output->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php new file mode 100644 index 00000000..5300783a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Doctrine\ORM\Mapping\Driver\DatabaseDriver; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert your mapping information between the various formats. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertMappingCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-mapping') + ->setAliases(array('orm:convert:mapping')) + ->setDescription('Convert mapping information between supported formats.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The mapping type to be converted.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your entities classes.' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + 'Force to overwrite existing mapping files.' + ), + new InputOption( + 'from-database', null, null, 'Whether or not to convert mapping information from existing database.' + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ), + new InputOption( + 'namespace', null, InputOption::VALUE_OPTIONAL, + 'Defines a namespace for the generated entity classes, if converted from database.' + ), + )) + ->setHelp(<<one-time command. It should not be necessary for +you to call this method multiple times, especially when using the --from-database +flag. + +Converting an existing database schema into mapping files only solves about 70-80% +of the necessary mapping information. Additionally the detection from an existing +database cannot detect inverse associations, inheritance types, +entities with foreign keys as primary keys and many of the +semantical operations on associations such as cascade. + +Hint: There is no need to convert YAML or XML mapping files to annotations +every time you make changes. All mapping drivers are first class citizens +in Doctrine 2 and can be used as runtime mapping for the ORM. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + if ($input->getOption('from-database') === true) { + $databaseDriver = new DatabaseDriver( + $em->getConnection()->getSchemaManager() + ); + + $em->getConfiguration()->setMetadataDriverImpl( + $databaseDriver + ); + + if (($namespace = $input->getOption('namespace')) !== null) { + $databaseDriver->setNamespace($namespace); + } + } + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); + + // Process destination directory + if ( ! is_dir($destPath = $input->getArgument('dest-path'))) { + mkdir($destPath, 0777, true); + } + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $toType = strtolower($input->getArgument('to-type')); + + $exporter = $this->getExporter($toType, $destPath); + $exporter->setOverwriteExistingFiles($input->getOption('force')); + + if ($toType == 'annotation') { + $entityGenerator = new EntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + if (count($metadata)) { + foreach ($metadata as $class) { + $output->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Exporting "%s" mapping information to "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } + + /** + * @param string $toType + * @param string $destPath + * + * @return \Doctrine\ORM\Tools\Export\Driver\AbstractExporter + */ + protected function getExporter($toType, $destPath) + { + $cme = new ClassMetadataExporter(); + + return $cme->getExporter($toType, $destPath); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php new file mode 100644 index 00000000..3601570e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to ensure that Doctrine is properly configured for a production environment. + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EnsureProductionSettingsCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:ensure-production-settings') + ->setDescription('Verify that Doctrine is properly configured for a production environment.') + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'Flag to also inspect database connection existence.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + + return 1; + } + + $output->writeln('Environment is correctly configured for production.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php new file mode 100644 index 00000000..a8fcac3c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -0,0 +1,164 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate entity classes and method stubs from your mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateEntitiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-entities') + ->setAliases(array('orm:generate:entities')) + ->setDescription('Generate entity classes and method stubs from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.' + ), + new InputOption( + 'generate-annotations', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate annotation metadata on entities.', false + ), + new InputOption( + 'generate-methods', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate stub methods on entities.', true + ), + new InputOption( + 'regenerate-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should regenerate entity if it exists.', false + ), + new InputOption( + 'update-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should only update entity if it exists.', true + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<--update-entities or --regenerate-entities flags your existing +code gets overwritten. The EntityGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the entity code if you are using entities as Data +Access Objects only and don't put much additional logic on them. If you are +however putting much more logic on the entities you should refrain from using +the entity-generator and code your entities manually. + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the entity +code manually for inheritance to work! +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadatas = $cmf->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + // Create EntityGenerator + $entityGenerator = new EntityGenerator(); + + $entityGenerator->setGenerateAnnotations($input->getOption('generate-annotations')); + $entityGenerator->setGenerateStubMethods($input->getOption('generate-methods')); + $entityGenerator->setRegenerateEntityIfExists($input->getOption('regenerate-entities')); + $entityGenerator->setUpdateEntityIfExists($input->getOption('update-entities')); + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Entities + $entityGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Entity classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php new file mode 100644 index 00000000..52211879 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -0,0 +1,115 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to (re)generate the proxy classes used by doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateProxiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-proxies') + ->setAliases(array('orm:generate:proxies')) + ->setDescription('Generates proxy classes for entity classes.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::OPTIONAL, + 'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.' + ), + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + if (($destPath = $input->getArgument('dest-path')) === null) { + $destPath = $em->getConfiguration()->getProxyDir(); + } + + if ( ! is_dir($destPath)) { + mkdir($destPath, 0777, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if ( count($metadatas)) { + foreach ($metadatas as $metadata) { + $output->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Proxies + $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php new file mode 100644 index 00000000..975bc693 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,117 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate repository classes for mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateRepositoriesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-repositories') + ->setAliases(array('orm:generate:repositories')) + ->setDescription('Generate repository classes from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + $numRepositories = 0; + $generator = new EntityRepositoryGenerator(); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->writeln( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) + ); + + $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) ); + } else { + $output->writeln('No Repository classes were found to be processed.' ); + } + } else { + $output->writeln('No Metadata Classes to process.' ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php new file mode 100644 index 00000000..f23fe22c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Show information about mapped entities. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + */ +class InfoCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:info') + ->setDescription('Show basic information about all mapped entities') + ->setHelp(<<%command.name% shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $entityManager \Doctrine\ORM\EntityManager */ + $entityManager = $this->getHelper('em')->getEntityManager(); + + $entityClassNames = $entityManager->getConfiguration() + ->getMetadataDriverImpl() + ->getAllClassNames(); + + if (!$entityClassNames) { + throw new \Exception( + 'You do not have any mapped Doctrine ORM entities according to the current configuration. '. + 'If you have entities or mapping files you should check your mapping configuration for errors.' + ); + } + + $output->writeln(sprintf("Found %d mapped entities:", count($entityClassNames))); + + $failure = false; + + foreach ($entityClassNames as $entityClassName) { + try { + $entityManager->getClassMetadata($entityClassName); + $output->writeln(sprintf("[OK] %s", $entityClassName)); + } catch (MappingException $e) { + $output->writeln("[FAIL] ".$entityClassName); + $output->writeln(sprintf("%s", $e->getMessage())); + $output->writeln(''); + + $failure = true; + } + } + + return $failure ? 1 : 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php new file mode 100644 index 00000000..1189b819 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Util\Debug; + +/** + * Command to execute DQL queries in a given EntityManager. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunDqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:run-dql') + ->setDescription('Executes arbitrary DQL directly from the command line.') + ->setDefinition(array( + new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'), + new InputOption( + 'hydrate', null, InputOption::VALUE_REQUIRED, + 'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.', + 'object' + ), + new InputOption( + 'first-result', null, InputOption::VALUE_REQUIRED, + 'The first result in the result set.' + ), + new InputOption( + 'max-result', null, InputOption::VALUE_REQUIRED, + 'The maximum number of results in the result set.' + ), + new InputOption( + 'depth', null, InputOption::VALUE_REQUIRED, + 'Dumping depth of Entity graph.', 7 + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + if (($dql = $input->getArgument('dql')) === null) { + throw new \RuntimeException("Argument 'DQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + $hydrationModeName = $input->getOption('hydrate'); + $hydrationMode = 'Doctrine\ORM\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName)); + + if ( ! defined($hydrationMode)) { + throw new \RuntimeException( + "Hydration mode '$hydrationModeName' does not exist. It should be either: object. array, scalar or single-scalar." + ); + } + + $query = $em->createQuery($dql); + + if (($firstResult = $input->getOption('first-result')) !== null) { + if ( ! is_numeric($firstResult)) { + throw new \LogicException("Option 'first-result' must contains an integer value"); + } + + $query->setFirstResult((int) $firstResult); + } + + if (($maxResult = $input->getOption('max-result')) !== null) { + if ( ! is_numeric($maxResult)) { + throw new \LogicException("Option 'max-result' must contains an integer value"); + } + + $query->setMaxResults((int) $maxResult); + } + + $resultSet = $query->execute(array(), constant($hydrationMode)); + + Debug::dump($resultSet, $input->getOption('depth')); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php new file mode 100644 index 00000000..0aabd630 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Base class for CreateCommand, DropCommand and UpdateCommand. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractCommand extends Command +{ + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param SchemaTool $schemaTool + * @param array $metadatas + * + * @return null|int Null or 0 if everything went fine, or an error code. + */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas); + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $emHelper = $this->getHelper('em'); + + /* @var $em \Doctrine\ORM\EntityManager */ + $em = $emHelper->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + + if ( ! empty($metadatas)) { + // Create SchemaTool + $tool = new SchemaTool($em); + + return $this->executeSchemaCommand($input, $output, $tool, $metadatas); + } else { + $output->writeln('No Metadata Classes to process.'); + return 0; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php new file mode 100644 index 00000000..610f25c9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to create the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CreateCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:create') + ->setDescription( + 'Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + ) + )) + ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + if ($input->getOption('dump-sql')) { + $sqls = $schemaTool->getCreateSchemaSql($metadatas); + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + $output->writeln('Creating database schema...'); + $schemaTool->createSchema($metadatas); + $output->writeln('Database schema created successfully!'); + } + + return 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php new file mode 100644 index 00000000..ac003b99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DropCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:drop') + ->setDescription( + 'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + "Don't ask for the deletion of the database, but force the operation to run." + ), + new InputOption( + 'full-database', null, InputOption::VALUE_NONE, + 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.' + ), + )) + ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + $isFullDatabaseDrop = $input->getOption('full-database'); + + if ($input->getOption('dump-sql')) { + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + $output->writeln(implode(';' . PHP_EOL, $sqls)); + + return 0; + } + + if ($input->getOption('force')) { + $output->writeln('Dropping database schema...'); + + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } + + $output->writeln('Database schema dropped successfully!'); + + return 0; + } + + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + + if (count($sqls)) { + $output->writeln('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.'); + $output->writeln('Please run the operation with --force to execute these queries or use --dump-sql to see them.'); + + return 1; + } + + $output->writeln('Nothing to drop. The database is empty!'); + + return 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php new file mode 100644 index 00000000..577fb9e0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -0,0 +1,155 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Ryan Weaver + */ +class UpdateCommand extends AbstractCommand +{ + /** + * @var string + */ + protected $name = 'orm:schema-tool:update'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName($this->name) + ->setDescription( + 'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.' + ) + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'If defined, all assets of the database which are not relevant to the current metadata will be dropped.' + ), + + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Dumps the generated SQL statements to the screen (does not execute them).' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + 'Causes the generated SQL statements to be physically executed against your database.' + ), + )); + + $this->setHelp(<<%command.name% command generates the SQL needed to +synchronize the database schema with the current mapping metadata of the +default entity manager. + +For example, if you add metadata for a new column to an entity, this command +would generate and output the SQL needed to add the new column to the database: + +%command.name% --dump-sql + +Alternatively, you can execute the generated queries: + +%command.name% --force + +If both options are specified, the queries are output and then executed: + +%command.name% --dump-sql --force + +Finally, be aware that if the --complete option is passed, this +task will drop all database assets (e.g. tables, etc) that are *not* described +by the current metadata. In other words, without this option, this task leaves +untouched any "extra" tables that exist in the database, but which aren't +described by any metadata. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + // Defining if update is complete or not (--complete not defined means $saveMode = true) + $saveMode = ! $input->getOption('complete'); + + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + + if (0 === count($sqls)) { + $output->writeln('Nothing to update - your database is already in sync with the current entity metadata.'); + + return 0; + } + + $dumpSql = true === $input->getOption('dump-sql'); + $force = true === $input->getOption('force'); + + if ($dumpSql) { + $output->writeln(implode(';' . PHP_EOL, $sqls)); + } + + if ($force) { + if ($dumpSql) { + $output->writeln(''); + } + $output->writeln('Updating database schema...'); + $schemaTool->updateSchema($metadatas, $saveMode); + $output->writeln(sprintf('Database schema updated successfully! "%s" queries were executed', count($sqls))); + } + + if ($dumpSql || $force) { + return 0; + } + + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); + $output->writeln(' Use the incremental update to detect changes during development and use'); + $output->writeln(' the SQL DDL provided to manually update your database in production.'); + $output->writeln(''); + + $output->writeln(sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls))); + $output->writeln('Please run the operation by passing one - or both - of the following options:'); + + $output->writeln(sprintf(' %s --force to execute the command', $this->getName())); + $output->writeln(sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName())); + + return 1; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php new file mode 100644 index 00000000..4bffbf76 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaValidator; + +/** + * Command to validate that the current mapping is valid. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ValidateSchemaCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:validate-schema') + ->setDescription('Validate the mapping files.') + ->setHelp(<<getHelper('em')->getEntityManager(); + + $validator = new SchemaValidator($em); + $errors = $validator->validateMapping(); + + $exit = 0; + if ($errors) { + foreach ($errors as $className => $errorMessages) { + $output->writeln("[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:"); + + foreach ($errorMessages as $errorMessage) { + $output->writeln('* ' . $errorMessage); + } + + $output->writeln(''); + } + + $exit += 1; + } else { + $output->writeln('[Mapping] OK - The mapping files are correct.'); + } + + if (!$validator->schemaInSyncWithMetadata()) { + $output->writeln('[Database] FAIL - The database schema is not in sync with the current mapping file.'); + $exit += 2; + } else { + $output->writeln('[Database] OK - The database schema is in sync with the mapping files.'); + } + + return $exit; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php new file mode 100644 index 00000000..365fcb41 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Version; +use Doctrine\ORM\EntityManager; + +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param EntityManager $entityManager + * @return HelperSet + */ + public static function createHelperSet(EntityManager $entityManager) + { + return new HelperSet(array( + 'db' => new ConnectionHelper($entityManager->getConnection()), + 'em' => new EntityManagerHelper($entityManager) + )); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = array()) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @param Application $cli + * + * @return void + */ + static public function addCommands(Application $cli) + { + $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(), + new \Doctrine\ORM\Tools\Console\Command\InfoCommand() + )); + } + + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\ORM\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\ORM\EntityManager; + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityManagerHelper extends Helper +{ + /** + * Doctrine ORM EntityManager. + * + * @var EntityManager + */ + protected $_em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + } + + /** + * Retrieves Doctrine ORM EntityManager. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'entityManager'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php new file mode 100644 index 00000000..4f7813c1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataFilter extends \FilterIterator implements \Countable +{ + /** + * @var array + */ + private $filter = array(); + + /** + * Filter Metadatas by one or more filter options. + * + * @param array $metadatas + * @param array|string $filter + * + * @return array + */ + static public function filter(array $metadatas, $filter) + { + $metadatas = new MetadataFilter(new \ArrayIterator($metadatas), $filter); + + return iterator_to_array($metadatas); + } + + /** + * @param \ArrayIterator $metadata + * @param array|string $filter + */ + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->filter = (array) $filter; + + parent::__construct($metadata); + } + + /** + * @return bool + */ + public function accept() + { + if (count($this->filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->filter as $filter) { + if (strpos($metadata->name, $filter) !== false) { + return true; + } + } + + return false; + } + + /** + * @return int + */ + public function count() + { + return count($this->getInnerIterator()); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php new file mode 100644 index 00000000..54094538 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -0,0 +1,344 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; +use Symfony\Component\Yaml\Yaml; + +/** + * Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1Schema +{ + /** + * @var array + */ + private $from; + + /** + * @var array + */ + private $legacyTypeMap = array( + // TODO: This list may need to be updated + 'clob' => 'text', + 'timestamp' => 'datetime', + 'enum' => 'string' + ); + + /** + * Constructor passes the directory or array of directories + * to convert the Doctrine 1 schema files from. + * + * @param array $from + * + * @author Jonathan Wage + */ + public function __construct($from) + { + $this->from = (array) $from; + } + + /** + * Gets an array of ClassMetadataInfo instances from the passed + * Doctrine 1 schema. + * + * @return array An array of ClassMetadataInfo instances + */ + public function getMetadata() + { + $schema = array(); + foreach ($this->from as $path) { + if (is_dir($path)) { + $files = glob($path . '/*.yml'); + foreach ($files as $file) { + $schema = array_merge($schema, (array) Yaml::parse($file)); + } + } else { + $schema = array_merge($schema, (array) Yaml::parse($path)); + } + } + + $metadatas = array(); + foreach ($schema as $className => $mappingInformation) { + $metadatas[] = $this->convertToClassMetadataInfo($className, $mappingInformation); + } + + return $metadatas; + } + + /** + * @param string $className + * @param array $mappingInformation + * + * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + private function convertToClassMetadataInfo($className, $mappingInformation) + { + $metadata = new ClassMetadataInfo($className); + + $this->convertTableName($className, $mappingInformation, $metadata); + $this->convertColumns($className, $mappingInformation, $metadata); + $this->convertIndexes($className, $mappingInformation, $metadata); + $this->convertRelations($className, $mappingInformation, $metadata); + + return $metadata; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertTableName($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['tableName']) && $model['tableName']) { + $e = explode('.', $model['tableName']); + + if (count($e) > 1) { + $metadata->table['schema'] = $e[0]; + $metadata->table['name'] = $e[1]; + } else { + $metadata->table['name'] = $e[0]; + } + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertColumns($className, array $model, ClassMetadataInfo $metadata) + { + $id = false; + + if (isset($model['columns']) && $model['columns']) { + foreach ($model['columns'] as $name => $column) { + $fieldMapping = $this->convertColumn($className, $name, $column, $metadata); + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $id = true; + } + } + } + + if ( ! $id) { + $fieldMapping = array( + 'fieldName' => 'id', + 'columnName' => 'id', + 'type' => 'integer', + 'id' => true + ); + $metadata->mapField($fieldMapping); + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + } + + /** + * @param string $className + * @param string $name + * @param string|array $column + * @param ClassMetadataInfo $metadata + * + * @return array + * + * @throws ToolsException + */ + private function convertColumn($className, $name, $column, ClassMetadataInfo $metadata) + { + if (is_string($column)) { + $string = $column; + $column = array(); + $column['type'] = $string; + } + + if ( ! isset($column['name'])) { + $column['name'] = $name; + } + + // check if a column alias was used (column_name as field_name) + if (preg_match("/(\w+)\sas\s(\w+)/i", $column['name'], $matches)) { + $name = $matches[1]; + $column['name'] = $name; + $column['alias'] = $matches[2]; + } + + if (preg_match("/([a-zA-Z]+)\(([0-9]+)\)/", $column['type'], $matches)) { + $column['type'] = $matches[1]; + $column['length'] = $matches[2]; + } + + $column['type'] = strtolower($column['type']); + // check if legacy column type (1.x) needs to be mapped to a 2.0 one + if (isset($this->legacyTypeMap[$column['type']])) { + $column['type'] = $this->legacyTypeMap[$column['type']]; + } + + if ( ! Type::hasType($column['type'])) { + throw ToolsException::couldNotMapDoctrine1Type($column['type']); + } + + $fieldMapping = array(); + + if (isset($column['primary'])) { + $fieldMapping['id'] = true; + } + + $fieldMapping['fieldName'] = isset($column['alias']) ? $column['alias'] : $name; + $fieldMapping['columnName'] = $column['name']; + $fieldMapping['type'] = $column['type']; + + if (isset($column['length'])) { + $fieldMapping['length'] = $column['length']; + } + + $allowed = array('precision', 'scale', 'unique', 'options', 'notnull', 'version'); + + foreach ($column as $key => $value) { + if (in_array($key, $allowed)) { + $fieldMapping[$key] = $value; + } + } + + $metadata->mapField($fieldMapping); + + if (isset($column['autoincrement'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } elseif (isset($column['sequence'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); + + $definition = array( + 'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence'] + ); + + if (isset($column['sequence']['size'])) { + $definition['allocationSize'] = $column['sequence']['size']; + } + + if (isset($column['sequence']['value'])) { + $definition['initialValue'] = $column['sequence']['value']; + } + + $metadata->setSequenceGeneratorDefinition($definition); + } + + return $fieldMapping; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertIndexes($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['indexes'])) { + return; + } + + foreach ($model['indexes'] as $name => $index) { + $type = (isset($index['type']) && $index['type'] == 'unique') + ? 'uniqueConstraints' : 'indexes'; + + $metadata->table[$type][$name] = array( + 'columns' => $index['fields'] + ); + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertRelations($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['relations'])) { + return; + } + + foreach ($model['relations'] as $name => $relation) { + if ( ! isset($relation['alias'])) { + $relation['alias'] = $name; + } + if ( ! isset($relation['class'])) { + $relation['class'] = $name; + } + if ( ! isset($relation['local'])) { + $relation['local'] = Inflector::tableize($relation['class']); + } + if ( ! isset($relation['foreign'])) { + $relation['foreign'] = 'id'; + } + if ( ! isset($relation['foreignAlias'])) { + $relation['foreignAlias'] = $className; + } + + if (isset($relation['refClass'])) { + $type = 'many'; + $foreignType = 'many'; + $joinColumns = array(); + } else { + $type = isset($relation['type']) ? $relation['type'] : 'one'; + $foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many'; + $joinColumns = array( + array( + 'name' => $relation['local'], + 'referencedColumnName' => $relation['foreign'], + 'onDelete' => isset($relation['onDelete']) ? $relation['onDelete'] : null, + ) + ); + } + + if ($type == 'one' && $foreignType == 'one') { + $method = 'mapOneToOne'; + } elseif ($type == 'many' && $foreignType == 'many') { + $method = 'mapManyToMany'; + } else { + $method = 'mapOneToMany'; + } + + $associationMapping = array(); + $associationMapping['fieldName'] = $relation['alias']; + $associationMapping['targetEntity'] = $relation['class']; + $associationMapping['mappedBy'] = $relation['foreignAlias']; + $associationMapping['joinColumns'] = $joinColumns; + + $metadata->$method($associationMapping); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php new file mode 100644 index 00000000..2b6478be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Proxy; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\EntityManager; + +/** + * Use this logger to dump the identity map during the onFlush event. This is useful for debugging + * weird UnitOfWork behavior with complex operations. + */ +class DebugUnitOfWorkListener +{ + /** + * @var string + */ + private $file; + + /** + * @var string + */ + private $context; + + /** + * Pass a stream and context information for the debugging session. + * + * The stream can be php://output to print to the screen. + * + * @param string $file + * @param string $context + */ + public function __construct($file = 'php://output', $context = '') + { + $this->file = $file; + $this->context = $context; + } + + /** + * @param \Doctrine\ORM\Event\OnFlushEventArgs $args + * + * @return void + */ + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dumps the contents of the identity map into a stream. + * + * @param EntityManager $em + * + * @return void + */ + public function dumpIdentityMap(EntityManager $em) + { + $uow = $em->getUnitOfWork(); + $identityMap = $uow->getIdentityMap(); + + $fh = fopen($this->file, "x+"); + if (count($identityMap) == 0) { + fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n"); + return; + } + + fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n"); + foreach ($identityMap as $className => $map) { + fwrite($fh, "Class: ". $className . "\n"); + + foreach ($map as $entity) { + fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n"); + fwrite($fh, " Associations:\n"); + + $cm = $em->getClassMetadata($className); + + foreach ($cm->associationMappings as $field => $assoc) { + fwrite($fh, " " . $field . " "); + $value = $cm->getFieldValue($entity, $field); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($value === null) { + fwrite($fh, " NULL\n"); + } else { + if ($value instanceof Proxy && !$value->__isInitialized()) { + fwrite($fh, "[PROXY] "); + } + + fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n"); + } + } else { + $initialized = !($value instanceof PersistentCollection) || $value->isInitialized(); + if ($value === null) { + fwrite($fh, " NULL\n"); + } elseif ($initialized) { + fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n"); + + foreach ($value as $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } else { + fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n"); + foreach ($value->unwrap() as $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } + } + } + } + } + + fclose($fh); + } + + /** + * @param mixed $var + * + * @return string + */ + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + + return $refl->getShortname(); + } + + return gettype($var); + } + + /** + * @param object $entity + * @param UnitOfWork $uow + * + * @return string + */ + private function getIdString($entity, UnitOfWork $uow) + { + if ($uow->isInIdentityMap($entity)) { + $ids = $uow->getEntityIdentifier($entity); + $idstring = ""; + + foreach ($ids as $k => $v) { + $idstring .= $k."=".$v; + } + } else { + $idstring = "NEWOBJECT "; + } + + $state = $uow->getEntityState($entity); + + if ($state == UnitOfWork::STATE_NEW) { + $idstring .= " [NEW]"; + } elseif ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } elseif ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } elseif ($state == UnitOfwork::STATE_DETACHED) { + $idstring .= " [DETACHED]"; + } + + return $idstring; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php new file mode 100644 index 00000000..7a3ec6f8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Mapping\StaticReflectionService; +use Doctrine\ORM\Mapping\ClassMetadataFactory; + +/** + * The DisconnectedClassMetadataFactory is used to create ClassMetadataInfo objects + * that do not require the entity class actually exist. This allows us to + * load some mapping information and use it to do things like generate code + * from the mapping information. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DisconnectedClassMetadataFactory extends ClassMetadataFactory +{ + /** + * @return \Doctrine\Common\Persistence\Mapping\StaticReflectionService + */ + public function getReflectionService() + { + return new StaticReflectionService(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php new file mode 100644 index 00000000..5a55f0fc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -0,0 +1,1504 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; + +/** + * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances. + * + * [php] + * $classes = $em->getClassMetadataFactory()->getAllMetadata(); + * + * $generator = new \Doctrine\ORM\Tools\EntityGenerator(); + * $generator->setGenerateAnnotations(true); + * $generator->setGenerateStubMethods(true); + * $generator->setRegenerateEntityIfExists(false); + * $generator->setUpdateEntityIfExists(true); + * $generator->generate($classes, '/path/to/generate/entities'); + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityGenerator +{ + /** + * Specifies class fields should be protected. + */ + const FIELD_VISIBLE_PROTECTED = 'protected'; + + /** + * Specifies class fields should be private. + */ + const FIELD_VISIBLE_PRIVATE = 'private'; + + /** + * @var bool + */ + protected $backupExisting = true; + + /** + * The extension to use for written php files. + * + * @var string + */ + protected $extension = '.php'; + + /** + * Whether or not the current ClassMetadataInfo instance is new or old. + * + * @var boolean + */ + protected $isNew = true; + + /** + * @var array + */ + protected $staticReflection = array(); + + /** + * Number of spaces to use for indention in generated code. + */ + protected $numSpaces = 4; + + /** + * The actual spaces to use for indention. + * + * @var string + */ + protected $spaces = ' '; + + /** + * The class all generated entities should extend. + * + * @var string + */ + protected $classToExtend; + + /** + * Whether or not to generation annotations. + * + * @var boolean + */ + protected $generateAnnotations = false; + + /** + * @var string + */ + protected $annotationsPrefix = ''; + + /** + * Whether or not to generate sub methods. + * + * @var boolean + */ + protected $generateEntityStubMethods = false; + + /** + * Whether or not to update the entity class if it exists already. + * + * @var boolean + */ + protected $updateEntityIfExists = false; + + /** + * Whether or not to re-generate entity class if it exists already. + * + * @var boolean + */ + protected $regenerateEntityIfExists = false; + + /** + * @var boolean + */ + protected $fieldVisibility = 'private'; + + /** + * Hash-map for handle types. + * + * @var array + */ + protected $typeAlias = array( + Type::DATETIMETZ => '\DateTime', + Type::DATETIME => '\DateTime', + Type::DATE => '\DateTime', + Type::TIME => '\DateTime', + Type::OBJECT => '\stdClass', + Type::BIGINT => 'integer', + Type::SMALLINT => 'integer', + Type::TEXT => 'string', + Type::BLOB => 'string', + Type::DECIMAL => 'float', + Type::JSON_ARRAY => 'array', + Type::SIMPLE_ARRAY => 'array', + ); + + /** + * Hash-map to handle generator types string. + * + * @var array + */ + protected static $generatorStrategyMap = array( + ClassMetadataInfo::GENERATOR_TYPE_AUTO => 'AUTO', + ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE => 'SEQUENCE', + ClassMetadataInfo::GENERATOR_TYPE_TABLE => 'TABLE', + ClassMetadataInfo::GENERATOR_TYPE_IDENTITY => 'IDENTITY', + ClassMetadataInfo::GENERATOR_TYPE_NONE => 'NONE', + ClassMetadataInfo::GENERATOR_TYPE_UUID => 'UUID', + ClassMetadataInfo::GENERATOR_TYPE_CUSTOM => 'CUSTOM' + ); + + /** + * Hash-map to handle the change tracking policy string. + * + * @var array + */ + protected static $changeTrackingPolicyMap = array( + ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT => 'DEFERRED_IMPLICIT', + ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT => 'DEFERRED_EXPLICIT', + ClassMetadataInfo::CHANGETRACKING_NOTIFY => 'NOTIFY', + ); + + /** + * Hash-map to handle the inheritance type string. + * + * @var array + */ + protected static $inheritanceTypeMap = array( + ClassMetadataInfo::INHERITANCE_TYPE_NONE => 'NONE', + ClassMetadataInfo::INHERITANCE_TYPE_JOINED => 'JOINED', + ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE => 'SINGLE_TABLE', + ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS', + ); + + /** + * @var string + */ + protected static $classTemplate = +' + +use Doctrine\ORM\Mapping as ORM; + + + +{ + +} +'; + + /** + * @var string + */ + protected static $getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + /** + * @var string + */ + protected static $setMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this-> = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $addMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this->[] = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $removeMethodTemplate = +'/** + * + * + * @param $ + */ +public function ($) +{ +$this->->removeElement($); +}'; + + /** + * @var string + */ + protected static $lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + /** + * @var string + */ + protected static $constructorMethodTemplate = +'/** + * Constructor + */ +public function __construct() +{ + +} +'; + + /** + * Constructor. + */ + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generates and writes entity classes for the given array of ClassMetadataInfo instances. + * + * @param array $metadatas + * @param string $outputDirectory + * + * @return void + */ + public function generate(array $metadatas, $outputDirectory) + { + foreach ($metadatas as $metadata) { + $this->writeEntityClass($metadata, $outputDirectory); + } + } + + /** + * Generates and writes entity class to disk for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * + * @return void + * + * @throws \RuntimeException + */ + public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory) + { + $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists); + + if ( ! $this->isNew) { + $this->parseTokensInEntityFile(file_get_contents($path)); + } else { + $this->staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array()); + } + + if ($this->backupExisting && file_exists($path)) { + $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~"; + if (!copy($path, $backupPath)) { + throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed."); + } + } + + // If entity doesn't exist or we're re-generating the entities entirely + if ($this->isNew) { + file_put_contents($path, $this->generateEntityClass($metadata)); + // If entity exists and we're allowed to update the entity class + } elseif ( ! $this->isNew && $this->updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + } + + /** + * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '' + ); + + $replacements = array( + $this->generateEntityNamespace($metadata), + $this->generateEntityDocBlock($metadata), + $this->generateEntityClassName($metadata), + $this->generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, self::$classTemplate); + + return str_replace('', $this->spaces, $code); + } + + /** + * Generates the updated code for the given ClassMetadataInfo and entity at path. + * + * @param ClassMetadataInfo $metadata + * @param string $path + * + * @return string + */ + public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path) + { + $currentCode = file_get_contents($path); + + $body = $this->generateEntityBody($metadata); + $body = str_replace('', $this->spaces, $body); + $last = strrpos($currentCode, '}'); + + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}\n"; + } + + /** + * Sets the number of spaces the exported class should have. + * + * @param integer $numSpaces + * + * @return void + */ + public function setNumSpaces($numSpaces) + { + $this->spaces = str_repeat(' ', $numSpaces); + $this->numSpaces = $numSpaces; + } + + /** + * Sets the extension to use when writing php files to disk. + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Sets the name of the class the generated classes should extend from. + * + * @param string $classToExtend + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->classToExtend = $classToExtend; + } + + /** + * Sets whether or not to generate annotations for the entity. + * + * @param bool $bool + * + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->generateAnnotations = $bool; + } + + /** + * Sets the class fields visibility for the entity (can either be private or protected). + * + * @param bool $visibility + * + * @return void + * + * @throws \InvalidArgumentException + */ + public function setFieldVisibility($visibility) + { + if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) { + throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); + } + + $this->fieldVisibility = $visibility; + } + + /** + * Sets an annotation prefix. + * + * @param string $prefix + * + * @return void + */ + public function setAnnotationPrefix($prefix) + { + $this->annotationsPrefix = $prefix; + } + + /** + * Sets whether or not to try and update the entity if it already exists. + * + * @param bool $bool + * + * @return void + */ + public function setUpdateEntityIfExists($bool) + { + $this->updateEntityIfExists = $bool; + } + + /** + * Sets whether or not to regenerate the entity if it exists. + * + * @param bool $bool + * + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->regenerateEntityIfExists = $bool; + } + + /** + * Sets whether or not to generate stub methods for the entity. + * + * @param bool $bool + * + * @return void + */ + public function setGenerateStubMethods($bool) + { + $this->generateEntityStubMethods = $bool; + } + + /** + * Should an existing entity be backed up if it already exists? + * + * @param bool $bool + * + * @return void + */ + public function setBackupExisting($bool) + { + $this->backupExisting = $bool; + } + + /** + * @param string $type + * + * @return string + */ + protected function getType($type) + { + if (isset($this->typeAlias[$type])) { + return $this->typeAlias[$type]; + } + + return $type; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->hasNamespace($metadata)) { + return 'namespace ' . $this->getNamespace($metadata) .';'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->getClassName($metadata) . + ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata); + $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->hasMethod('__construct', $metadata)) { + return ''; + } + + $collections = array(); + + foreach ($metadata->associationMappings as $mapping) { + if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { + $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; + } + } + + if ($collections) { + return $this->prefixCodeWithSpaces(str_replace("", implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate)); + } + + return ''; + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * + * @param string $src + * + * @return void + */ + protected function parseTokensInEntityFile($src) + { + $tokens = token_get_all($src); + $lastSeenNamespace = ""; + $lastSeenClass = false; + + $inNamespace = false; + $inClass = false; + + for ($i = 0; $i < count($tokens); $i++) { + $token = $tokens[$i]; + if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + if ($inNamespace) { + if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) { + $lastSeenNamespace .= $token[1]; + } elseif (is_string($token) && in_array($token, array(';', '{'))) { + $inNamespace = false; + } + } + + if ($inClass) { + $inClass = false; + $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1]; + $this->staticReflection[$lastSeenClass]['properties'] = array(); + $this->staticReflection[$lastSeenClass]['methods'] = array(); + } + + if ($token[0] == T_NAMESPACE) { + $lastSeenNamespace = ""; + $inNamespace = true; + } elseif ($token[0] == T_CLASS) { + $inClass = true; + } elseif ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1]; + } elseif ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1]; + } + } elseif (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) { + $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1); + } + } + } + + /** + * @param string $property + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->extendsClass()) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend()); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($property, $this->staticReflection[$metadata->name]['properties']) + ); + } + + /** + * @param string $method + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->extendsClass()) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend()); + + if ($reflClass->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($method, $this->staticReflection[$metadata->name]['methods']) + ); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + /** + * @return bool + */ + protected function extendsClass() + { + return $this->classToExtend ? true : false; + } + + /** + * @return string + */ + protected function getClassToExtend() + { + return $this->classToExtend; + } + + /** + * @return string + */ + protected function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + + return '\\' . $refl->getName(); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * ' . $this->getClassName($metadata); + + if ($this->generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + 'generateTableAnnotation', + 'generateInheritanceAnnotation', + 'generateDiscriminatorColumnAnnotation', + 'generateDiscriminatorMapAnnotation' + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if ($metadata->isMappedSuperclass) { + $lines[] = ' * @' . $this->annotationsPrefix . 'MappedSuperClass'; + } else { + $lines[] = ' * @' . $this->annotationsPrefix . 'Entity'; + } + + if ($metadata->customRepositoryClassName) { + $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")'; + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateTableAnnotation($metadata) + { + $table = array(); + + if (isset($metadata->table['schema'])) { + $table[] = 'schema="' . $metadata->table['schema'] . '"'; + } + + if (isset($metadata->table['name'])) { + $table[] = 'name="' . $metadata->table['name'] . '"'; + } + + if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) { + $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']); + $table[] = 'uniqueConstraints={' . $constraints . '}'; + } + + if (isset($metadata->table['indexes']) && $metadata->table['indexes']) { + $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']); + $table[] = 'indexes={' . $constraints . '}'; + } + + return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')'; + } + + /** + * @param string $constraintName + * @param array $constraints + * + * @return string + */ + protected function generateTableConstraints($constraintName, $constraints) + { + $annotations = array(); + foreach ($constraints as $name => $constraint) { + $columns = array(); + foreach ($constraint['columns'] as $column) { + $columns[] = '"' . $column . '"'; + } + $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})'; + } + return implode(', ', $annotations); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorValue; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateDiscriminatorMapAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $inheritanceClassMap = array(); + + foreach ($metadata->discriminatorMap as $type => $class) { + $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"'; + } + + return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) { + if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null; + if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + /** + * @param array $associationMapping + * + * @return bool + */ + protected function isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo there is no way to retrieve targetEntity metadata + $joinColumns = array(); + } + + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + + return true; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata) + { + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $methods = array(); + + foreach ($metadata->lifecycleCallbacks as $name => $callbacks) { + foreach ($callbacks as $callback) { + if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + return ""; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($this->hasProperty($associationMapping['fieldName'], $metadata)) { + continue; + } + + $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + + $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * @param string $type + * @param string $fieldName + * @param string|null $typeHint + * @param string|null $defaultValue + * + * @return string + */ + protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + $methodName = $type . Inflector::classify($fieldName); + if (in_array($type, array("add", "remove"))) { + $methodName = Inflector::singularize($methodName); + } + + if ($this->hasMethod($methodName, $metadata)) { + return ''; + } + $this->staticReflection[$metadata->name]['methods'][] = $methodName; + + $var = sprintf('%sMethodTemplate', $type); + $template = self::$$var; + + $methodTypeHint = null; + $types = Type::getTypesMap(); + $variableType = $typeHint ? $this->getType($typeHint) . ' ' : null; + + if ($typeHint && ! isset($types[$typeHint])) { + $variableType = '\\' . ltrim($variableType, '\\'); + $methodTypeHint = '\\' . $typeHint . ' '; + } + + $replacements = array( + '' => ucfirst($type) . ' ' . $fieldName, + '' => $methodTypeHint, + '' => $variableType, + '' => Inflector::camelize($fieldName), + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param string $name + * @param string $methodName + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateLifecycleCallbackMethod($name, $methodName, $metadata) + { + if ($this->hasMethod($methodName, $metadata)) { + return ''; + } + $this->staticReflection[$metadata->name]['methods'][] = $methodName; + + $replacements = array( + '' => $this->annotationsPrefix . ucfirst($name), + '' => $methodName, + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + self::$lifecycleCallbackMethodTemplate + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param array $joinColumn + * + * @return string + */ + protected function generateJoinColumnAnnotation(array $joinColumn) + { + $joinColumnAnnot = array(); + + if (isset($joinColumn['name'])) { + $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; + } + + if (isset($joinColumn['referencedColumnName'])) { + $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; + } + + if (isset($joinColumn['unique']) && $joinColumn['unique']) { + $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false'); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); + } + + if (isset($joinColumn['onDelete'])) { + $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"'); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"'; + } + + return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; + } + + /** + * @param array $associationMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + + if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection'; + } else { + $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\'); + } + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + if (isset($associationMapping['id']) && $associationMapping['id']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; + + if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + } + + $type = null; + switch ($associationMapping['type']) { + case ClassMetadataInfo::ONE_TO_ONE: + $type = 'OneToOne'; + break; + case ClassMetadataInfo::MANY_TO_ONE: + $type = 'ManyToOne'; + break; + case ClassMetadataInfo::ONE_TO_MANY: + $type = 'OneToMany'; + break; + case ClassMetadataInfo::MANY_TO_MANY: + $type = 'ManyToMany'; + break; + } + $typeOptions = array(); + + if (isset($associationMapping['targetEntity'])) { + $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; + } + + if (isset($associationMapping['inversedBy'])) { + $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; + } + + if (isset($associationMapping['mappedBy'])) { + $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; + } + + if ($associationMapping['cascade']) { + $cascades = array(); + + if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; + + if (count($cascades) === 5) { + $cascades = array('"all"'); + } + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); + } + + if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) { + $fetchMap = array( + ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY', + ClassMetadataInfo::FETCH_EAGER => 'EAGER', + ); + + $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"'; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; + + if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) { + $joinColumnsLines[] = $this->spaces . ' * ' . $joinColumnAnnot; + } + } + + $lines[] = implode(",\n", $joinColumnsLines); + $lines[] = $this->spaces . ' * })'; + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTable = array(); + $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; + + if (isset($associationMapping['joinTable']['schema'])) { + $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ','; + $lines[] = $this->spaces . ' * joinColumns={'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $joinColumnsLines); + $lines[] = $this->spaces . ' * },'; + $lines[] = $this->spaces . ' * inverseJoinColumns={'; + + $inverseJoinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $inverseJoinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines); + $lines[] = $this->spaces . ' * }'; + $lines[] = $this->spaces . ' * )'; + } + + if (isset($associationMapping['orderBy'])) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({'; + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $lines[] = $this->spaces . ' * "' . $name . '"="' . $direction . '",'; + } + + $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1); + $lines[] = $this->spaces . ' * })'; + } + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param array $fieldMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']); + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $column = array(); + if (isset($fieldMapping['columnName'])) { + $column[] = 'name="' . $fieldMapping['columnName'] . '"'; + } + + if (isset($fieldMapping['type'])) { + $column[] = 'type="' . $fieldMapping['type'] . '"'; + } + + if (isset($fieldMapping['length'])) { + $column[] = 'length=' . $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $column[] = 'precision=' . $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $column[] = 'scale=' . $fieldMapping['scale']; + } + + if (isset($fieldMapping['nullable'])) { + $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); + } + + if (isset($fieldMapping['columnDefinition'])) { + $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; + } + + if (isset($fieldMapping['unique'])) { + $column[] = 'unique=' . var_export($fieldMapping['unique'], true); + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')'; + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; + + if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + + if ($metadata->sequenceGeneratorDefinition) { + $sequenceGenerator = array(); + + if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { + $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; + } + + if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { + $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize']; + } + + if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { + $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue']; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; + } + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version'; + } + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param string $code + * @param int $num + * + * @return string + */ + protected function prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + if ( ! empty($value)) { + $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key]; + } + } + + return implode("\n", $lines); + } + + /** + * @param integer $type The inheritance type used by the class and its subclasses. + * + * @return string The literal string for the inheritance type. + * + * @throws \InvalidArgumentException When the inheritance type does not exists. + */ + protected function getInheritanceTypeString($type) + { + if ( ! isset(self::$inheritanceTypeMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); + } + + return self::$inheritanceTypeMap[$type]; + } + + /** + * @param integer $type The policy used for change-tracking for the mapped class. + * + * @return string The literal string for the change-tracking type. + * + * @throws \InvalidArgumentException When the change-tracking type does not exists. + */ + protected function getChangeTrackingPolicyString($type) + { + if ( ! isset(self::$changeTrackingPolicyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); + } + + return self::$changeTrackingPolicyMap[$type]; + } + + /** + * @param integer $type The generator to use for the mapped class. + * + * @return string The literal string for the generator type. + * + * @throws \InvalidArgumentException When the generator type does not exists. + */ + protected function getIdGeneratorTypeString($type) + { + if ( ! isset(self::$generatorStrategyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); + } + + return self::$generatorStrategyMap[$type]; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php new file mode 100644 index 00000000..5093cd54 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +/** + * Class to generate entity repository classes + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepositoryGenerator +{ + protected static $_template = +' + +use Doctrine\ORM\EntityRepository; + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends EntityRepository +{ +} +'; + + /** + * @param string $fullClassName + * + * @return string + */ + public function generateEntityRepositoryClass($fullClassName) + { + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + + $variables = array( + '' => $this->generateEntityRepositoryNamespace($fullClassName), + '' => $className + ); + + return str_replace(array_keys($variables), array_values($variables), self::$_template); + } + + /** + * Generates the namespace statement, if class do not have namespace, return empty string instead. + * + * @param string $fullClassName The full repository class name. + * + * @return string $namespace + */ + private function generateEntityRepositoryNamespace($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + + return $namespace ? 'namespace ' . $namespace . ';' : ''; + } + + /** + * @param string $fullClassName + * @param string $outputDirectory + * + * @return void + */ + public function writeEntityRepositoryClass($fullClassName, $outputDirectory) + { + $code = $this->generateEntityRepositoryClass($fullClassName); + + $path = $outputDirectory . DIRECTORY_SEPARATOR + . str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php'; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php new file mode 100644 index 00000000..36455621 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\ORM\EntityManager; + +/** + * Event Args used for the Events::postGenerateSchema event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @param EntityManager $em + * @param Schema $schema + */ + public function __construct(EntityManager $em, Schema $schema) + { + $this->em = $em; + $this->schema = $schema; + } + + /** + * @return EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * @return Schema + */ + public function getSchema() + { + return $this->schema; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php new file mode 100644 index 00000000..9e68ae6e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -0,0 +1,86 @@ +. + */ +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Args used for the Events::postGenerateSchemaTable event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaTableEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $classTable; + + /** + * @param ClassMetadata $classMetadata + * @param Schema $schema + * @param Table $classTable + */ + public function __construct(ClassMetadata $classMetadata, Schema $schema, Table $classTable) + { + $this->classMetadata = $classMetadata; + $this->schema = $schema; + $this->classTable = $classTable; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * @return Schema + */ + public function getSchema() + { + return $this->schema; + } + + /** + * @return Table + */ + public function getClassTable() + { + return $this->classTable; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php new file mode 100644 index 00000000..478ae847 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export; + +use Doctrine\ORM\Tools\Export\ExportException; + +/** + * Class used for converting your mapping information between the + * supported formats: yaml, xml, and php/annotation. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class ClassMetadataExporter +{ + /** + * @var array + */ + private static $_exporterDrivers = array( + 'xml' => 'Doctrine\ORM\Tools\Export\Driver\XmlExporter', + 'yaml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'yml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'php' => 'Doctrine\ORM\Tools\Export\Driver\PhpExporter', + 'annotation' => 'Doctrine\ORM\Tools\Export\Driver\AnnotationExporter' + ); + + /** + * Registers a new exporter driver class under a specified name. + * + * @param string $name + * @param string $class + * + * @return void + */ + public static function registerExportDriver($name, $class) + { + self::$_exporterDrivers[$name] = $class; + } + + /** + * Gets an exporter driver instance. + * + * @param string $type The type to get (yml, xml, etc.). + * @param string|null $dest The directory where the exporter will export to. + * + * @return Driver\AbstractExporter + * + * @throws ExportException + */ + public function getExporter($type, $dest = null) + { + if ( ! isset(self::$_exporterDrivers[$type])) { + throw ExportException::invalidExporterDriverType($type); + } + + $class = self::$_exporterDrivers[$type]; + + return new $class($dest); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php new file mode 100644 index 00000000..d40d0786 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -0,0 +1,249 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\Export\ExportException; + +/** + * Abstract base class which is to be used for the Exporter drivers + * which can be found in \Doctrine\ORM\Tools\Export\Driver. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +abstract class AbstractExporter +{ + /** + * @var array + */ + protected $_metadata = array(); + + /** + * @var string|null + */ + protected $_outputDir; + + /** + * @var string|null + */ + protected $_extension; + + /** + * @var bool + */ + protected $_overwriteExistingFiles = false; + + /** + * @param string|null $dir + */ + public function __construct($dir = null) + { + $this->_outputDir = $dir; + } + + /** + * @param bool $overwrite + * + * @return void + */ + public function setOverwriteExistingFiles($overwrite) + { + $this->_overwriteExistingFiles = $overwrite; + } + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); + + /** + * Sets the array of ClassMetadataInfo instances to export. + * + * @param array $metadata + * + * @return void + */ + public function setMetadata(array $metadata) + { + $this->_metadata = $metadata; + } + + /** + * Gets the extension used to generated the path to a class. + * + * @return string|null + */ + public function getExtension() + { + return $this->_extension; + } + + /** + * Sets the directory to output the mapping files to. + * + * [php] + * $exporter = new YamlExporter($metadata); + * $exporter->setOutputDir(__DIR__ . '/yaml'); + * $exporter->export(); + * + * @param string $dir + * + * @return void + */ + public function setOutputDir($dir) + { + $this->_outputDir = $dir; + } + + /** + * Exports each ClassMetadata instance to a single Doctrine Mapping file + * named after the entity. + * + * @return void + * + * @throws \Doctrine\ORM\Tools\Export\ExportException + */ + public function export() + { + if ( ! is_dir($this->_outputDir)) { + mkdir($this->_outputDir, 0777, true); + } + + foreach ($this->_metadata as $metadata) { + //In case output is returned, write it to a file, skip otherwise + if($output = $this->exportClassMetadata($metadata)){ + $path = $this->_generateOutputPath($metadata); + $dir = dirname($path); + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + if (file_exists($path) && !$this->_overwriteExistingFiles) { + throw ExportException::attemptOverwriteExistingFile($path); + } + file_put_contents($path, $output); + } + } + } + + /** + * Generates the path to write the class for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; + } + + /** + * Sets the directory to output the mapping files to. + * + * [php] + * $exporter = new YamlExporter($metadata, __DIR__ . '/yaml'); + * $exporter->setExtension('.yml'); + * $exporter->export(); + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + /** + * @param int $type + * + * @return string + */ + protected function _getInheritanceTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + } + } + + /** + * @param int $policy + * + * @return string + */ + protected function _getChangeTrackingPolicyString($policy) + { + switch ($policy) { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + } + } + + /** + * @param int $type + * + * @return string + */ + protected function _getIdGeneratorTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + + case ClassMetadataInfo::GENERATOR_TYPE_UUID: + return 'UUID'; + + case ClassMetadataInfo::GENERATOR_TYPE_CUSTOM: + return 'CUSTOM'; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php new file mode 100644 index 00000000..044a1da5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\EntityGenerator; + +/** + * ClassMetadata exporter for PHP classes with annotations. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class AnnotationExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * @var EntityGenerator|null + */ + private $_entityGenerator; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + if ( ! $this->_entityGenerator) { + throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.'); + } + + $this->_entityGenerator->setGenerateAnnotations(true); + $this->_entityGenerator->setGenerateStubMethods(false); + $this->_entityGenerator->setRegenerateEntityIfExists(false); + $this->_entityGenerator->setUpdateEntityIfExists(false); + + return $this->_entityGenerator->generateEntityClass($metadata); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; + } + + /** + * @param \Doctrine\ORM\Tools\EntityGenerator $entityGenerator + * + * @return void + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->_entityGenerator = $entityGenerator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php new file mode 100644 index 00000000..778c30f4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -0,0 +1,173 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for PHP code. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class PhpExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = 'isMappedSuperclass) { + $lines[] = '$metadata->isMappedSuperclass = true;'; + } + + if ($metadata->inheritanceType) { + $lines[] = '$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_' . $this->_getInheritanceTypeString($metadata->inheritanceType) . ');'; + } + + if ($metadata->customRepositoryClassName) { + $lines[] = "\$metadata->customRepositoryClassName = '" . $metadata->customRepositoryClassName . "';"; + } + + if ($metadata->table) { + $lines[] = '$metadata->setPrimaryTable(' . $this->_varExport($metadata->table) . ');'; + } + + if ($metadata->discriminatorColumn) { + $lines[] = '$metadata->setDiscriminatorColumn(' . $this->_varExport($metadata->discriminatorColumn) . ');'; + } + + if ($metadata->discriminatorMap) { + $lines[] = '$metadata->setDiscriminatorMap(' . $this->_varExport($metadata->discriminatorMap) . ');'; + } + + if ($metadata->changeTrackingPolicy) { + $lines[] = '$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_' . $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . ');'; + } + + if ($metadata->lifecycleCallbacks) { + foreach ($metadata->lifecycleCallbacks as $event => $callbacks) { + foreach ($callbacks as $callback) { + $lines[] = "\$metadata->addLifecycleCallback('$callback', '$event');"; + } + } + } + + foreach ($metadata->fieldMappings as $fieldMapping) { + $lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');'; + } + + if ( ! $metadata->isIdentifierComposite && $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');'; + } + + foreach ($metadata->associationMappings as $associationMapping) { + $cascade = array('remove', 'persist', 'refresh', 'merge', 'detach'); + foreach ($cascade as $key => $value) { + if ( ! $associationMapping['isCascade'.ucfirst($value)]) { + unset($cascade[$key]); + } + } + + if (count($cascade) === 5) { + $cascade = array('all'); + } + + $associationMappingArray = array( + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $method = 'mapOneToOne'; + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $associationMapping['joinColumns'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $method = 'mapOneToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'orphanRemoval', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $oneToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $method = 'mapManyToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'joinTable', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $manyToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + } + + $lines[] = '$metadata->' . $method . '(' . $this->_varExport($associationMappingArray) . ');'; + } + + return implode("\n", $lines); + } + + /** + * @param mixed $var + * + * @return string + */ + protected function _varExport($var) + { + $export = var_export($var, true); + $export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export); + $export = str_replace(' ', ' ', $export); + $export = str_replace('array (', 'array(', $export); + $export = str_replace('array( ', 'array(', $export); + $export = str_replace(',)', ')', $export); + $export = str_replace(', )', ')', $export); + $export = str_replace(' ', ' ', $export); + + return $export; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php new file mode 100644 index 00000000..1a8fde18 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -0,0 +1,396 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine XML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class XmlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.xml'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $xml = new \SimpleXmlElement(""); + + /*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); + $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/ + + if ($metadata->isMappedSuperclass) { + $root = $xml->addChild('mapped-superclass'); + } else { + $root = $xml->addChild('entity'); + } + + if ($metadata->customRepositoryClassName) { + $root->addAttribute('repository-class', $metadata->customRepositoryClassName); + } + + $root->addAttribute('name', $metadata->name); + + if (isset($metadata->table['name'])) { + $root->addAttribute('table', $metadata->table['name']); + } + + if (isset($metadata->table['schema'])) { + $root->addAttribute('schema', $metadata->table['schema']); + } + + if (isset($metadata->table['inheritance-type'])) { + $root->addAttribute('inheritance-type', $metadata->table['inheritance-type']); + } + + if ($metadata->discriminatorColumn) { + $discriminatorColumnXml = $root->addChild('discriminator-column'); + $discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']); + $discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']); + + if (isset($metadata->discriminatorColumn['length'])) { + $discriminatorColumnXml->addAttribute('length', $metadata->discriminatorColumn['length']); + } + } + + if ($metadata->discriminatorMap) { + $discriminatorMapXml = $root->addChild('discriminator-map'); + + foreach ($metadata->discriminatorMap as $value => $className) { + $discriminatorMappingXml = $discriminatorMapXml->addChild('discriminator-mapping'); + $discriminatorMappingXml->addAttribute('value', $value); + $discriminatorMappingXml->addAttribute('class', $className); + } + } + + $trackingPolicy = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + + if ( $trackingPolicy != 'DEFERRED_IMPLICIT') { + $root->addChild('change-tracking-policy', $trackingPolicy); + } + + if (isset($metadata->table['indexes'])) { + $indexesXml = $root->addChild('indexes'); + + foreach ($metadata->table['indexes'] as $name => $index) { + $indexXml = $indexesXml->addChild('index'); + $indexXml->addAttribute('name', $name); + $indexXml->addAttribute('columns', implode(',', $index['columns'])); + } + } + + if (isset($metadata->table['uniqueConstraints'])) { + $uniqueConstraintsXml = $root->addChild('unique-constraints'); + + foreach ($metadata->table['uniqueConstraints'] as $name => $unique) { + $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); + $uniqueConstraintXml->addAttribute('name', $name); + $uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns'])); + } + } + + $fields = $metadata->fieldMappings; + + $id = array(); + foreach ($fields as $name => $field) { + if (isset($field['id']) && $field['id']) { + $id[$name] = $field; + unset($fields[$name]); + } + } + + foreach ($metadata->associationMappings as $name => $assoc) { + if (isset($assoc['id']) && $assoc['id']) { + $id[$name] = array( + 'fieldName' => $name, + 'associationKey' => true + ); + } + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + if ($id) { + foreach ($id as $field) { + $idXml = $root->addChild('id'); + $idXml->addAttribute('name', $field['fieldName']); + + if (isset($field['type'])) { + $idXml->addAttribute('type', $field['type']); + } + + if (isset($field['columnName'])) { + $idXml->addAttribute('column', $field['columnName']); + } + + if (isset($field['length'])) { + $idXml->addAttribute('length', $field['length']); + } + + if (isset($field['associationKey']) && $field['associationKey']) { + $idXml->addAttribute('association-key', 'true'); + } + + if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $generatorXml = $idXml->addChild('generator'); + $generatorXml->addAttribute('strategy', $idGeneratorType); + } + } + } + + if ($fields) { + foreach ($fields as $field) { + $fieldXml = $root->addChild('field'); + $fieldXml->addAttribute('name', $field['fieldName']); + $fieldXml->addAttribute('type', $field['type']); + + if (isset($field['columnName'])) { + $fieldXml->addAttribute('column', $field['columnName']); + } + + if (isset($field['length'])) { + $fieldXml->addAttribute('length', $field['length']); + } + + if (isset($field['precision'])) { + $fieldXml->addAttribute('precision', $field['precision']); + } + + if (isset($field['scale'])) { + $fieldXml->addAttribute('scale', $field['scale']); + } + + if (isset($field['unique']) && $field['unique']) { + $fieldXml->addAttribute('unique', $field['unique']); + } + + if (isset($field['options'])) { + $optionsXml = $fieldXml->addChild('options'); + foreach ($field['options'] as $key => $value) { + $optionsXml->addAttribute($key, $value); + } + } + + if (isset($field['version'])) { + $fieldXml->addAttribute('version', $field['version']); + } + + if (isset($field['columnDefinition'])) { + $fieldXml->addAttribute('column-definition', $field['columnDefinition']); + } + + if (isset($field['nullable'])) { + $fieldXml->addAttribute('nullable', $field['nullable'] ? 'true' : 'false'); + } + } + } + + $orderMap = array( + ClassMetadataInfo::ONE_TO_ONE, + ClassMetadataInfo::ONE_TO_MANY, + ClassMetadataInfo::MANY_TO_ONE, + ClassMetadataInfo::MANY_TO_MANY, + ); + + uasort($metadata->associationMappings, function($m1, $m2) use (&$orderMap){ + $a1 = array_search($m1['type'], $orderMap); + $a2 = array_search($m2['type'], $orderMap); + + return strcmp($a1, $a2); + }); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { + $associationMappingXml = $root->addChild('one-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $associationMappingXml = $root->addChild('one-to-many'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $associationMappingXml = $root->addChild('many-to-many'); + } + + $associationMappingXml->addAttribute('field', $associationMapping['fieldName']); + $associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']); + + if (isset($associationMapping['mappedBy'])) { + $associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']); + } + + if (isset($associationMapping['inversedBy'])) { + $associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']); + } + + if (isset($associationMapping['indexBy'])) { + $associationMappingXml->addAttribute('index-by', $associationMapping['indexBy']); + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval'] !== false) { + $associationMappingXml->addAttribute('orphan-removal', 'true'); + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTableXml = $associationMappingXml->addChild('join-table'); + $joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']); + $joinColumnsXml = $joinTableXml->addChild('join-columns'); + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + } + + $inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns'); + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + $inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column'); + $inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']); + $inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']); + + if (isset($inverseJoinColumn['onDelete'])) { + $inverseJoinColumnXml->addAttribute('on-delete', $inverseJoinColumn['onDelete']); + } + + if (isset($inverseJoinColumn['columnDefinition'])) { + $inverseJoinColumnXml->addAttribute('column-definition', $inverseJoinColumn['columnDefinition']); + } + + if (isset($inverseJoinColumn['nullable'])) { + $inverseJoinColumnXml->addAttribute('nullable', $inverseJoinColumn['nullable']); + } + + if (isset($inverseJoinColumn['orderBy'])) { + $inverseJoinColumnXml->addAttribute('order-by', $inverseJoinColumn['orderBy']); + } + } + } + if (isset($associationMapping['joinColumns'])) { + $joinColumnsXml = $associationMappingXml->addChild('join-columns'); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnXml->addAttribute('column-definition', $joinColumn['columnDefinition']); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnXml->addAttribute('nullable', $joinColumn['nullable']); + } + } + } + if (isset($associationMapping['orderBy'])) { + $orderByXml = $associationMappingXml->addChild('order-by'); + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $orderByFieldXml = $orderByXml->addChild('order-by-field'); + $orderByFieldXml->addAttribute('name', $name); + $orderByFieldXml->addAttribute('direction', $direction); + } + } + $cascade = array(); + + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'cascade-remove'; + } + + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'cascade-persist'; + } + + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'cascade-refresh'; + } + + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'cascade-merge'; + } + + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'cascade-detach'; + } + + if (count($cascade) === 5) { + $cascade = array('cascade-all'); + } + + if ($cascade) { + $cascadeXml = $associationMappingXml->addChild('cascade'); + + foreach ($cascade as $type) { + $cascadeXml->addChild($type); + } + } + } + + if (isset($metadata->lifecycleCallbacks) && count($metadata->lifecycleCallbacks)>0) { + $lifecycleCallbacksXml = $root->addChild('lifecycle-callbacks'); + + foreach ($metadata->lifecycleCallbacks as $name => $methods) { + foreach ($methods as $method) { + $lifecycleCallbackXml = $lifecycleCallbacksXml->addChild('lifecycle-callback'); + $lifecycleCallbackXml->addAttribute('type', $name); + $lifecycleCallbackXml->addAttribute('method', $method); + } + } + } + + return $this->_asXml($xml); + } + + /** + * @param \SimpleXMLElement $simpleXml + * + * @return string $xml + */ + private function _asXml($simpleXml) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; + + return $dom->saveXML(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php new file mode 100644 index 00000000..45c43192 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -0,0 +1,211 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Symfony\Component\Yaml\Yaml; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine YAML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class YamlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $array = array(); + + if ($metadata->isMappedSuperclass) { + $array['type'] = 'mappedSuperclass'; + } else { + $array['type'] = 'entity'; + } + + $array['table'] = $metadata->table['name']; + + if (isset($metadata->table['schema'])) { + $array['schema'] = $metadata->table['schema']; + } + + $inheritanceType = $metadata->inheritanceType; + + if ($inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $array['inheritanceType'] = $this->_getInheritanceTypeString($inheritanceType); + } + + if ($column = $metadata->discriminatorColumn) { + $array['discriminatorColumn'] = $column; + } + + if ($map = $metadata->discriminatorMap) { + $array['discriminatorMap'] = $map; + } + + if ($metadata->changeTrackingPolicy !== ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT) { + $array['changeTrackingPolicy'] = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + } + + if (isset($metadata->table['indexes'])) { + $array['indexes'] = $metadata->table['indexes']; + } + + if ($metadata->customRepositoryClassName) { + $array['repositoryClass'] = $metadata->customRepositoryClassName; + } + + if (isset($metadata->table['uniqueConstraints'])) { + $array['uniqueConstraints'] = $metadata->table['uniqueConstraints']; + } + + $fieldMappings = $metadata->fieldMappings; + + $ids = array(); + foreach ($fieldMappings as $name => $fieldMapping) { + $fieldMapping['column'] = $fieldMapping['columnName']; + + unset($fieldMapping['columnName'], $fieldMapping['fieldName']); + + if ($fieldMapping['column'] == $name) { + unset($fieldMapping['column']); + } + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $ids[$name] = $fieldMapping; + unset($fieldMappings[$name]); + continue; + } + + $fieldMappings[$name] = $fieldMapping; + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + $array['id'] = $ids; + + if ($fieldMappings) { + if ( ! isset($array['fields'])) { + $array['fields'] = array(); + } + $array['fields'] = array_merge($array['fields'], $fieldMappings); + } + + foreach ($metadata->associationMappings as $name => $associationMapping) { + $cascade = array(); + + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'remove'; + } + + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'persist'; + } + + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'refresh'; + } + + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'merge'; + } + + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'detach'; + } + if (count($cascade) === 5) { + $cascade = array('all'); + } + + $associationMappingArray = array( + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if (isset($mapping['id']) && $mapping['id'] === true) { + $array['id'][$name]['associationKey'] = true; + } + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $joinColumns = $associationMapping['joinColumns']; + $newJoinColumns = array(); + + foreach ($joinColumns as $joinColumn) { + $newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName']; + + if (isset($joinColumn['onDelete'])) { + $newJoinColumns[$joinColumn['name']]['onDelete'] = $joinColumn['onDelete']; + } + } + + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $newJoinColumns, + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + + if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) { + $array['oneToOne'][$name] = $associationMappingArray; + } else { + $array['manyToOne'][$name] = $associationMappingArray; + } + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $oneToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + $array['oneToMany'][$name] = $associationMappingArray; + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $manyToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null, + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + $array['manyToMany'][$name] = $associationMappingArray; + } + } + if (isset($metadata->lifecycleCallbacks)) { + $array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks; + } + + return Yaml::dump(array($metadata->name => $array), 10); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php new file mode 100644 index 00000000..89ddfe22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php @@ -0,0 +1,38 @@ + FROM ()) + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class CountOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT). + * + * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL) + * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery + * that will most likely be executed next can be read from the native SQL cache. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($this->platform->getName() === "mssql") { + $AST->orderByClause = null; + } + + $sql = parent::walkSelectStatement($AST); + + // Find out the SQL alias of the identifier column of the root entity + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT + // so for now, It's not supported. + + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = array(); + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + // Build the counter query + return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table', + $this->platform->getCountExpression('*'), + implode(', ', $sqlIdentifier), + $sql + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php new file mode 100644 index 00000000..778dd307 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php @@ -0,0 +1,93 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class CountWalker extends TreeWalkerAdapter +{ + /** + * Distinct mode hint name. + */ + const HINT_DISTINCT = 'doctrine_paginator.distinct'; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve a COUNT. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($AST->havingClause) { + throw new \RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); + } + + $rootComponents = array(); + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $identifierFieldName + ); + $pathExpression->type = $pathType; + + $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT); + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, $distinct), null + ) + ); + + // ORDER BY is not needed, only increases query execution through unnecessary sorting. + $AST->orderByClause = null; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php new file mode 100644 index 00000000..a6c24ee3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -0,0 +1,214 @@ + FROM () LIMIT x OFFSET y + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class LimitSubqueryOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * @var int + */ + private $firstResult; + + /** + * @var int + */ + private $maxResults; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + // Reset limit and offset + $this->firstResult = $query->getFirstResult(); + $this->maxResults = $query->getMaxResults(); + $query->setFirstResult(null)->setMaxResults(null); + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $innerSql = parent::walkSelectStatement($AST); + + // Find out the SQL alias of the identifier column of the root entity. + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT. + // So for now, it's not supported. + + // Get the root entity and alias from the AST fromClause. + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) !== 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = array(); + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + // Build the counter query + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); + + if ($this->platform instanceof PostgreSqlPlatform || + $this->platform instanceof OraclePlatform) { + //http://www.doctrine-project.org/jira/browse/DDC-1958 + $this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql); + } + + // Apply the limit and offset. + $sql = $this->platform->modifyLimitQuery( + $sql, $this->maxResults, $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + return $sql; + } + + /** + * Generates new SQL for Postgresql or Oracle if necessary. + * + * @param SelectStatement $AST + * @param array $sqlIdentifier + * @param string $innerSql + * @param string $sql + * + * @return void + */ + public function preserveSqlOrdering(SelectStatement $AST, array $sqlIdentifier, $innerSql, &$sql) + { + // For every order by, find out the SQL alias by inspecting the ResultSetMapping. + $sqlOrderColumns = array(); + $orderBy = array(); + if (isset($AST->orderByClause)) { + foreach ($AST->orderByClause->orderByItems as $item) { + $possibleAliases = (is_object($item->expression)) + ? array_keys($this->rsm->fieldMappings, $item->expression->field) + : array_keys($this->rsm->scalarMappings, $item->expression); + + foreach ($possibleAliases as $alias) { + if (!is_object($item->expression) || $this->rsm->columnOwnerMap[$alias] == $item->expression->identificationVariable) { + $sqlOrderColumns[] = $alias; + $orderBy[] = $alias . ' ' . $item->type; + break; + } + } + } + //remove identifier aliases + $sqlOrderColumns = array_diff($sqlOrderColumns, $sqlIdentifier); + } + + // We don't need orderBy in inner query. + // However at least on 5.4.6 I'm getting a segmentation fault and thus we don't clear it for now. + /*$AST->orderByClause = null; + $innerSql = parent::walkSelectStatement($AST);*/ + + if (count($orderBy)) { + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s', + implode(', ', array_merge($sqlIdentifier, $sqlOrderColumns)), + $innerSql, + implode(', ', $orderBy) + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php new file mode 100644 index 00000000..ca3f1032 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -0,0 +1,119 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\SelectExpression; +use Doctrine\ORM\Query\AST\PathExpression; + +/** + * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent. + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class LimitSubqueryWalker extends TreeWalkerAdapter +{ + /** + * ID type hint. + */ + const IDENTIFIER_TYPE = 'doctrine_paginator.id.type'; + + /** + * Counter for generating unique order column aliases. + * + * @var int + */ + private $_aliasCounter = 0; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids + * of the root Entity. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $parent = null; + $parentName = null; + $selectExpressions = array(); + + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + // Preserve mixed data in query for ordering. + if (isset($qComp['resultVariable'])) { + $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); + continue; + } + + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + continue; + } + } + + $identifier = $parent['metadata']->getSingleIdentifierFieldName(); + if (isset($parent['metadata']->associationMappings[$identifier])) { + throw new \RuntimeException("Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."); + } + + $this->_getQuery()->setHint( + self::IDENTIFIER_TYPE, + Type::getType($parent['metadata']->getTypeOfField($identifier)) + ); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $parentName, + $identifier + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id')); + $AST->selectClause->selectExpressions = $selectExpressions; + + if (isset($AST->orderByClause)) { + foreach ($AST->orderByClause->orderByItems as $item) { + if ($item->expression instanceof PathExpression) { + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $item->expression->identificationVariable, + $item->expression->field + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + $AST->selectClause->selectExpressions[] = new SelectExpression( + $pathExpression, + '_dctrn_ord' . $this->_aliasCounter++ + ); + } + } + } + + $AST->selectClause->isDistinct = true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php new file mode 100644 index 00000000..115eb590 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -0,0 +1,234 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\NoResultException; + +/** + * The paginator can handle various complex scenarios with DQL. + * + * @author Pablo Díez + * @author Benjamin Eberlei + * @license New BSD + */ +class Paginator implements \Countable, \IteratorAggregate +{ + /** + * @var Query + */ + private $query; + + /** + * @var bool + */ + private $fetchJoinCollection; + + /** + * @var bool|null + */ + private $useOutputWalkers; + + /** + * @var int + */ + private $count; + + /** + * Constructor. + * + * @param Query|QueryBuilder $query A Doctrine ORM query or query builder. + * @param boolean $fetchJoinCollection Whether the query joins a collection (true by default). + */ + public function __construct($query, $fetchJoinCollection = true) + { + if ($query instanceof QueryBuilder) { + $query = $query->getQuery(); + } + + $this->query = $query; + $this->fetchJoinCollection = (Boolean) $fetchJoinCollection; + } + + /** + * Returns the query. + * + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * Returns whether the query joins a collection. + * + * @return boolean Whether the query joins a collection. + */ + public function getFetchJoinCollection() + { + return $this->fetchJoinCollection; + } + + /** + * Returns whether the paginator will use an output walker. + * + * @return bool|null + */ + public function getUseOutputWalkers() + { + return $this->useOutputWalkers; + } + + /** + * Sets whether the paginator will use an output walker. + * + * @param bool|null $useOutputWalkers + * + * @return $this + */ + public function setUseOutputWalkers($useOutputWalkers) + { + $this->useOutputWalkers = $useOutputWalkers; + return $this; + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->count === null) { + /* @var $countQuery Query */ + $countQuery = $this->cloneQuery($this->query); + + if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) { + $countQuery->setHint(CountWalker::HINT_DISTINCT, true); + } + + if ($this->useOutputWalker($countQuery)) { + $platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win + + $rsm = new ResultSetMapping(); + $rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count'); + + $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker'); + $countQuery->setResultSetMapping($rsm); + } else { + $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + } + + $countQuery->setFirstResult(null)->setMaxResults(null); + + try { + $data = $countQuery->getScalarResult(); + $data = array_map('current', $data); + $this->count = array_sum($data); + } catch(NoResultException $e) { + $this->count = 0; + } + } + + return $this->count; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $offset = $this->query->getFirstResult(); + $length = $this->query->getMaxResults(); + + if ($this->fetchJoinCollection) { + $subQuery = $this->cloneQuery($this->query); + + if ($this->useOutputWalker($subQuery)) { + $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + } else { + $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')); + } + + $subQuery->setFirstResult($offset)->setMaxResults($length); + + $ids = array_map('current', $subQuery->getScalarResult()); + + $whereInQuery = $this->cloneQuery($this->query); + // don't do this for an empty id array + if (count($ids) == 0) { + return new \ArrayIterator(array()); + } + + $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker')); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)); + $whereInQuery->setFirstResult(null)->setMaxResults(null); + $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids); + + $result = $whereInQuery->getResult($this->query->getHydrationMode()); + } else { + $result = $this->cloneQuery($this->query) + ->setMaxResults($length) + ->setFirstResult($offset) + ->getResult($this->query->getHydrationMode()) + ; + } + + return new \ArrayIterator($result); + } + + /** + * Clones a query. + * + * @param Query $query The query. + * + * @return Query The cloned query. + */ + private function cloneQuery(Query $query) + { + /* @var $cloneQuery Query */ + $cloneQuery = clone $query; + + $cloneQuery->setParameters(clone $query->getParameters()); + + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } + + return $cloneQuery; + } + + /** + * Determines whether to use an output walker for the query. + * + * @param Query $query The query. + * + * @return bool + */ + private function useOutputWalker(Query $query) + { + if ($this->useOutputWalkers === null) { + return (Boolean) $query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER) == false; + } + + return $this->useOutputWalkers; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php new file mode 100644 index 00000000..27cbc3c2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php @@ -0,0 +1,147 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\Query\AST\ArithmeticExpression; +use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\InExpression; +use Doctrine\ORM\Query\AST\NullComparisonExpression; +use Doctrine\ORM\Query\AST\InputParameter; +use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\ConditionalTerm; +use Doctrine\ORM\Query\AST\ConditionalExpression; +use Doctrine\ORM\Query\AST\ConditionalFactor; +use Doctrine\ORM\Query\AST\WhereClause; + +/** + * Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent. + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class WhereInWalker extends TreeWalkerAdapter +{ + /** + * ID Count hint name. + */ + const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count'; + + /** + * Primary key alias for query. + */ + const PAGINATOR_ID_ALIAS = 'dpid'; + + /** + * Replaces the whereClause in the AST. + * + * Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...) + * + * The parameter namespace (dpid) is defined by + * the PAGINATOR_ID_ALIAS + * + * The total number of parameters is retrieved from + * the HINT_PAGINATOR_ID_COUNT query hint. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $rootComponents = array(); + foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, $identifierFieldName); + $pathExpression->type = $pathType; + + $count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT); + + if ($count > 0) { + $arithmeticExpression = new ArithmeticExpression(); + $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression( + array($pathExpression) + ); + $expression = new InExpression($arithmeticExpression); + $expression->literals[] = new InputParameter(":" . self::PAGINATOR_ID_ALIAS); + + } else { + $expression = new NullComparisonExpression($pathExpression); + $expression->not = false; + } + + $conditionalPrimary = new ConditionalPrimary; + $conditionalPrimary->simpleConditionalExpression = $expression; + if ($AST->whereClause) { + if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) { + $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary; + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) { + $AST->whereClause->conditionalExpression = new ConditionalExpression(array( + new ConditionalTerm(array( + $AST->whereClause->conditionalExpression, + $conditionalPrimary + )) + )); + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression + || $AST->whereClause->conditionalExpression instanceof ConditionalFactor + ) { + $tmpPrimary = new ConditionalPrimary; + $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression; + $AST->whereClause->conditionalExpression = new ConditionalTerm(array( + $tmpPrimary, + $conditionalPrimary + )); + } + } else { + $AST->whereClause = new WhereClause( + new ConditionalExpression(array( + new ConditionalTerm(array( + $conditionalPrimary + )) + )) + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php new file mode 100644 index 00000000..e193ae2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * ResolveTargetEntityListener + * + * Mechanism to overwrite interfaces or classes specified as association + * targets. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class ResolveTargetEntityListener +{ + /** + * @var array + */ + private $resolveTargetEntities = array(); + + /** + * Adds a target-entity class name to resolve to a new class name. + * + * @param string $originalEntity + * @param string $newEntity + * @param array $mapping + * + * @return void + */ + public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping) + { + $mapping['targetEntity'] = ltrim($newEntity, "\\"); + $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; + } + + /** + * Processes event and resolves new target entity names. + * + * @param LoadClassMetadataEventArgs $args + * + * @return void + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $args) + { + $cm = $args->getClassMetadata(); + + foreach ($cm->associationMappings as $mapping) { + if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + $this->remapAssociation($cm, $mapping); + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata + * @param array $mapping + * + * @return void + */ + private function remapAssociation($classMetadata, $mapping) + { + $newMapping = $this->resolveTargetEntities[$mapping['targetEntity']]; + $newMapping = array_replace_recursive($mapping, $newMapping); + $newMapping['fieldName'] = $mapping['fieldName']; + + unset($classMetadata->associationMappings[$mapping['fieldName']]); + + switch ($mapping['type']) { + case ClassMetadata::MANY_TO_MANY: + $classMetadata->mapManyToMany($newMapping); + break; + case ClassMetadata::MANY_TO_ONE: + $classMetadata->mapManyToOne($newMapping); + break; + case ClassMetadata::ONE_TO_MANY: + $classMetadata->mapOneToMany($newMapping); + break; + case ClassMetadata::ONE_TO_ONE: + $classMetadata->mapOneToOne($newMapping); + break; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php new file mode 100644 index 00000000..21140f7a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -0,0 +1,867 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Internal\CommitOrderCalculator; +use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * The SchemaTool is a tool to create/drop/update database schemas based on + * ClassMetadata class descriptors. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Stefano Rodriguez + */ +class SchemaTool +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * Initializes a new SchemaTool instance that uses the connection of the + * provided EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->platform = $em->getConnection()->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Creates the database schema for the given array of ClassMetadata instances. + * + * @param array $classes + * + * @return void + * + * @throws ToolsException + */ + public function createSchema(array $classes) + { + $createSchemaSql = $this->getCreateSchemaSql($classes); + $conn = $this->em->getConnection(); + + foreach ($createSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch (\Exception $e) { + throw ToolsException::schemaToolFailure($sql, $e); + } + } + } + + /** + * Gets the list of DDL statements that are required to create the database schema for + * the given list of ClassMetadata instances. + * + * @param array $classes + * + * @return array The SQL statements needed to create the schema for the classes. + */ + public function getCreateSchemaSql(array $classes) + { + $schema = $this->getSchemaFromMetadata($classes); + return $schema->toSql($this->platform); + } + + /** + * Detects instances of ClassMetadata that don't need to be processed in the SchemaTool context. + * + * @param ClassMetadata $class + * @param array $processedClasses + * + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + + /** + * Creates a Schema instance from a given set of metadata classes. + * + * @param array $classes + * + * @return Schema + * + * @throws \Doctrine\ORM\ORMException + */ + public function getSchemaFromMetadata(array $classes) + { + // Reminder for processed classes, used for hierarchies + $processedClasses = array(); + $eventManager = $this->em->getEventManager(); + $schemaManager = $this->em->getConnection()->getSchemaManager(); + $metadataSchemaConfig = $schemaManager->createSchemaConfig(); + + $metadataSchemaConfig->setExplicitForeignKeyIndexes(false); + $schema = new Schema(array(), array(), $metadataSchemaConfig); + + $addedFks = array(); + $blacklistedFks = array(); + + foreach ($classes as $class) { + /** @var \Doctrine\ORM\Mapping\ClassMetadata $class */ + if ($this->processingNotRequired($class, $processedClasses)) { + continue; + } + + $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); + + if ($class->isInheritanceTypeSingleTable()) { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // Add the discriminator column + $this->addDiscriminatorColumnDefinition($class, $table); + + // Aggregate all the information from all classes in the hierarchy + foreach ($class->parentClasses as $parentClassName) { + // Parent class information is already contained in this class + $processedClasses[$parentClassName] = true; + } + + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $this->gatherColumns($subClass, $table); + $this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks); + $processedClasses[$subClassName] = true; + } + } elseif ($class->isInheritanceTypeJoined()) { + // Add all non-inherited fields as columns + $pkColumns = array(); + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited'])) { + $columnName = $this->quoteStrategy->getColumnName( + $mapping['fieldName'], + $class, + $this->platform + ); + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($fieldName)) { + $pkColumns[] = $columnName; + } + } + } + + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // Add the discriminator column only to the root table + if ($class->name == $class->rootEntityName) { + $this->addDiscriminatorColumnDefinition($class, $table); + } else { + // Add an ID FK column to child tables + $inheritedKeyColumns = array(); + foreach ($class->identifier as $identifierField) { + $idMapping = $class->fieldMappings[$identifierField]; + if (isset($idMapping['inherited'])) { + $this->gatherColumn($class, $idMapping, $table); + $columnName = $this->quoteStrategy->getColumnName( + $identifierField, + $class, + $this->platform + ); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); + + $pkColumns[] = $columnName; + $inheritedKeyColumns[] = $columnName; + } + } + if (!empty($inheritedKeyColumns)) { + // Add a FK constraint on the ID column + $table->addForeignKeyConstraint( + $this->quoteStrategy->getTableName( + $this->em->getClassMetadata($class->rootEntityName), + $this->platform + ), + $inheritedKeyColumns, + $inheritedKeyColumns, + array('onDelete' => 'CASCADE') + ); + } + + } + + $table->setPrimaryKey($pkColumns); + + } elseif ($class->isInheritanceTypeTablePerClass()) { + throw ORMException::notSupported(); + } else { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + } + + $pkColumns = array(); + foreach ($class->identifier as $identifierField) { + if (isset($class->fieldMappings[$identifierField])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform); + } elseif (isset($class->associationMappings[$identifierField])) { + /* @var $assoc \Doctrine\ORM\Mapping\OneToOne */ + $assoc = $class->associationMappings[$identifierField]; + foreach ($assoc['joinColumns'] as $joinColumn) { + $pkColumns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + } + } + + if ( ! $table->hasIndex('primary')) { + $table->setPrimaryKey($pkColumns); + } + + if (isset($class->table['indexes'])) { + foreach ($class->table['indexes'] as $indexName => $indexData) { + $table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + if (isset($class->table['uniqueConstraints'])) { + foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) { + $table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + if (isset($class->table['options'])) { + foreach ($class->table['options'] as $key => $val) { + $table->addOption($key, $val); + } + } + + $processedClasses[$class->name] = true; + + if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { + $seqDef = $class->sequenceGeneratorDefinition; + $quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform); + if ( ! $schema->hasSequence($quotedName)) { + $schema->createSequence( + $quotedName, + $seqDef['allocationSize'], + $seqDef['initialValue'] + ); + } + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchemaTable, + new GenerateSchemaTableEventArgs($class, $schema, $table) + ); + } + } + + if ( ! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas() ) { + $schema->visit(new RemoveNamespacedAssets()); + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchema, + new GenerateSchemaEventArgs($this->em, $schema) + ); + } + + return $schema; + } + + /** + * Gets a portable column definition as required by the DBAL for the discriminator + * column of a class. + * + * @param ClassMetadata $class + * @param Table $table + * + * @return array The portable column definition of the discriminator column as required by + * the DBAL. + */ + private function addDiscriminatorColumnDefinition($class, Table $table) + { + $discrColumn = $class->discriminatorColumn; + + if ( ! isset($discrColumn['type']) || + (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null) + ) { + $discrColumn['type'] = 'string'; + $discrColumn['length'] = 255; + } + + $options = array( + 'length' => isset($discrColumn['length']) ? $discrColumn['length'] : null, + 'notnull' => true + ); + + if (isset($discrColumn['columnDefinition'])) { + $options['columnDefinition'] = $discrColumn['columnDefinition']; + } + + $table->addColumn($discrColumn['name'], $discrColumn['type'], $options); + } + + /** + * Gathers the column definitions as required by the DBAL of all field mappings + * found in the given class. + * + * @param ClassMetadata $class + * @param Table $table + * + * @return array The list of portable column definitions as required by the DBAL. + */ + private function gatherColumns($class, Table $table) + { + $pkColumns = array(); + + foreach ($class->fieldMappings as $mapping) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + continue; + } + + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($mapping['fieldName'])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); + } + } + + // For now, this is a hack required for single table inheritence, since this method is called + // twice by single table inheritence relations + if (!$table->hasIndex('primary')) { + //$table->setPrimaryKey($pkColumns); + } + } + + /** + * Creates a column definition as required by the DBAL from an ORM field mapping definition. + * + * @param ClassMetadata $class The class that owns the field mapping. + * @param array $mapping The field mapping. + * @param Table $table + * + * @return array The portable column definition as required by the DBAL. + */ + private function gatherColumn($class, array $mapping, Table $table) + { + $columnName = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); + $columnType = $mapping['type']; + + $options = array(); + $options['length'] = isset($mapping['length']) ? $mapping['length'] : null; + $options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; + if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) { + $options['notnull'] = false; + } + + $options['platformOptions'] = array(); + $options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; + + if (strtolower($columnType) == 'string' && $options['length'] === null) { + $options['length'] = 255; + } + + if (isset($mapping['precision'])) { + $options['precision'] = $mapping['precision']; + } + + if (isset($mapping['scale'])) { + $options['scale'] = $mapping['scale']; + } + + if (isset($mapping['default'])) { + $options['default'] = $mapping['default']; + } + + if (isset($mapping['columnDefinition'])) { + $options['columnDefinition'] = $mapping['columnDefinition']; + } + + if (isset($mapping['options'])) { + $knownOptions = array('comment', 'unsigned', 'fixed', 'default'); + + foreach ($knownOptions as $knownOption) { + if ( isset($mapping['options'][$knownOption])) { + $options[$knownOption] = $mapping['options'][$knownOption]; + + unset($mapping['options'][$knownOption]); + } + } + + $options['customSchemaOptions'] = $mapping['options']; + } + + if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) { + $options['autoincrement'] = true; + } + if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) { + $options['autoincrement'] = false; + } + + if ($table->hasColumn($columnName)) { + // required in some inheritance scenarios + $table->changeColumn($columnName, $options); + } else { + $table->addColumn($columnName, $columnType, $options); + } + + $isUnique = isset($mapping['unique']) ? $mapping['unique'] : false; + if ($isUnique) { + $table->addUniqueIndex(array($columnName)); + } + } + + /** + * Gathers the SQL for properly setting up the relations of the given class. + * This includes the SQL for foreign key constraints and join tables. + * + * @param ClassMetadata $class + * @param Table $table + * @param Schema $schema + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks) + { + foreach ($class->associationMappings as $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $foreignClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + $primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type + + $this->gatherRelationJoinColumns( + $mapping['joinColumns'], + $table, + $foreignClass, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); + + foreach ($uniqueConstraints as $indexName => $unique) { + $table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } elseif ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { + //... create join table, one-many through join table supported later + throw ORMException::notSupported(); + } elseif ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { + // create join table + $joinTable = $mapping['joinTable']; + + $theJoinTable = $schema->createTable( + $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform) + ); + + $primaryKeyColumns = $uniqueConstraints = array(); + + // Build first FK constraint (relation table => source table) + $this->gatherRelationJoinColumns( + $joinTable['joinColumns'], + $theJoinTable, + $class, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); + + // Build second FK constraint (relation table => target table) + $this->gatherRelationJoinColumns( + $joinTable['inverseJoinColumns'], + $theJoinTable, + $foreignClass, + $mapping, + $primaryKeyColumns, + $uniqueConstraints, + $addedFks, + $blacklistedFks + ); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); + + foreach ($uniqueConstraints as $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } + } + } + + /** + * Gets the class metadata that is responsible for the definition of the referenced column name. + * + * Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its + * not a simple field, go through all identifier field names that are associations recursively and + * find that referenced column name. + * + * TODO: Is there any way to make this code more pleasing? + * + * @param ClassMetadata $class + * @param string $referencedColumnName + * + * @return array (ClassMetadata, referencedFieldName) + */ + private function getDefiningClass($class, $referencedColumnName) + { + $referencedFieldName = $class->getFieldName($referencedColumnName); + + if ($class->hasField($referencedFieldName)) { + return array($class, $referencedFieldName); + } + + if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) { + // it seems to be an entity as foreign key + foreach ($class->getIdentifierFieldNames() as $fieldName) { + if ($class->hasAssociation($fieldName) + && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) { + return $this->getDefiningClass( + $this->em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']), + $class->getSingleAssociationReferencedJoinColumnName($fieldName) + ); + } + } + } + + return null; + } + + /** + * Gathers columns and fk constraints that are required for one part of relationship. + * + * @param array $joinColumns + * @param Table $theJoinTable + * @param ClassMetadata $class + * @param array $mapping + * @param array $primaryKeyColumns + * @param array $uniqueConstraints + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationJoinColumns( + $joinColumns, + $theJoinTable, + $class, + $mapping, + &$primaryKeyColumns, + &$uniqueConstraints, + &$addedFks, + &$blacklistedFks + ) { + $localColumns = array(); + $foreignColumns = array(); + $fkOptions = array(); + $foreignTableName = $this->quoteStrategy->getTableName($class, $this->platform); + + foreach ($joinColumns as $joinColumn) { + + list($definingClass, $referencedFieldName) = $this->getDefiningClass( + $class, + $joinColumn['referencedColumnName'] + ); + + if ( ! $definingClass) { + throw new \Doctrine\ORM\ORMException( + "Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ". + $mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist." + ); + } + + $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( + $joinColumn, + $class, + $this->platform + ); + + $primaryKeyColumns[] = $quotedColumnName; + $localColumns[] = $quotedColumnName; + $foreignColumns[] = $quotedRefColumnName; + + if ( ! $theJoinTable->hasColumn($quotedColumnName)) { + // Only add the column to the table if it does not exist already. + // It might exist already if the foreign key is mapped into a regular + // property as well. + + $fieldMapping = $definingClass->getFieldMapping($referencedFieldName); + + $columnDef = null; + if (isset($joinColumn['columnDefinition'])) { + $columnDef = $joinColumn['columnDefinition']; + } elseif (isset($fieldMapping['columnDefinition'])) { + $columnDef = $fieldMapping['columnDefinition']; + } + + $columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef); + + if (isset($joinColumn['nullable'])) { + $columnOptions['notnull'] = !$joinColumn['nullable']; + } + + if (isset($fieldMapping['options'])) { + $columnOptions['options'] = $fieldMapping['options']; + } + + if ($fieldMapping['type'] == "string" && isset($fieldMapping['length'])) { + $columnOptions['length'] = $fieldMapping['length']; + } elseif ($fieldMapping['type'] == "decimal") { + $columnOptions['scale'] = $fieldMapping['scale']; + $columnOptions['precision'] = $fieldMapping['precision']; + } + + $theJoinTable->addColumn($quotedColumnName, $fieldMapping['type'], $columnOptions); + } + + if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) { + $uniqueConstraints[] = array('columns' => array($quotedColumnName)); + } + + if (isset($joinColumn['onDelete'])) { + $fkOptions['onDelete'] = $joinColumn['onDelete']; + } + } + + $compositeName = $theJoinTable->getName().'.'.implode('', $localColumns); + if (isset($addedFks[$compositeName]) + && ($foreignTableName != $addedFks[$compositeName]['foreignTableName'] + || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns']))) + ) { + foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { + if (0 === count(array_diff($key->getLocalColumns(), $localColumns)) + && (($key->getForeignTableName() != $foreignTableName) + || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns))) + ) { + $theJoinTable->removeForeignKey($fkName); + break; + } + } + $blacklistedFks[$compositeName] = true; + } elseif (!isset($blacklistedFks[$compositeName])) { + $addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns); + $theJoinTable->addUnnamedForeignKeyConstraint( + $foreignTableName, + $localColumns, + $foreignColumns, + $fkOptions + ); + } + } + + /** + * Drops the database schema for the given classes. + * + * In any way when an exception is thrown it is suppressed since drop was + * issued for all classes of the schema and some probably just don't exist. + * + * @param array $classes + * + * @return void + */ + public function dropSchema(array $classes) + { + $dropSchemaSql = $this->getDropSchemaSQL($classes); + $conn = $this->em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch (\Exception $e) { + + } + } + } + + /** + * Drops all elements in the database of the current connection. + * + * @return void + */ + public function dropDatabase() + { + $dropSchemaSql = $this->getDropDatabaseSQL(); + $conn = $this->em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the SQL needed to drop the database schema for the connections database. + * + * @return array + */ + public function getDropDatabaseSQL() + { + $sm = $this->em->getConnection()->getSchemaManager(); + $schema = $sm->createSchema(); + + $visitor = new DropSchemaSqlCollector($this->platform); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * Gets SQL to drop the tables defined by the passed classes. + * + * @param array $classes + * + * @return array + */ + public function getDropSchemaSQL(array $classes) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $schema = $this->getSchemaFromMetadata($classes); + + $sm = $this->em->getConnection()->getSchemaManager(); + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ( ! $schema->hasTable($table->getName())) { + foreach ($table->getForeignKeys() as $foreignKey) { + /* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */ + if ($schema->hasTable($foreignKey->getForeignTableName())) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } else { + $visitor->acceptTable($table); + foreach ($table->getForeignKeys() as $foreignKey) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } + + if ($this->platform->supportsSequences()) { + foreach ($schema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($schema->getTables() as $table) { + /* @var $sequence Table */ + if ($table->hasPrimaryKey()) { + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) == 1) { + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + } + } + } + + return $visitor->getQueries(); + } + + /** + * Updates the database schema of the given classes by comparing the ClassMetadata + * instances to the current database schema that is inspected. If $saveMode is set + * to true the command is executed in the Database, else SQL is returned. + * + * @param array $classes + * @param boolean $saveMode + * + * @return void + */ + public function updateSchema(array $classes, $saveMode = false) + { + $updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode); + $conn = $this->em->getConnection(); + + foreach ($updateSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the sequence of SQL statements that need to be performed in order + * to bring the given class mappings in-synch with the relational schema. + * If $saveMode is set to true the command is executed in the Database, + * else SQL is returned. + * + * @param array $classes The classes to consider. + * @param boolean $saveMode True for writing to DB, false for SQL string. + * + * @return array The sequence of SQL statements. + */ + public function getUpdateSchemaSql(array $classes, $saveMode = false) + { + $sm = $this->em->getConnection()->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $toSchema = $this->getSchemaFromMetadata($classes); + + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($saveMode) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php new file mode 100644 index 00000000..328aeb23 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -0,0 +1,271 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Types\Type; + +/** + * Performs strict validation of the mapping schema + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SchemaValidator +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Checks the internal consistency of all mapping files. + * + * There are several checks that can't be done at runtime or are too expensive, which can be verified + * with this command. For example: + * + * 1. Check if a relation with "mappedBy" is actually connected to that specified field. + * 2. Check if "mappedBy" and "inversedBy" are consistent to each other. + * 3. Check if "referencedColumnName" attributes are really pointing to primary key columns. + * 4. Check if there are public properties that might cause problems with lazy loading. + * + * @return array + */ + public function validateMapping() + { + $errors = array(); + $cmf = $this->em->getMetadataFactory(); + $classes = $cmf->getAllMetadata(); + + foreach ($classes as $class) { + if ($ce = $this->validateClass($class)) { + $errors[$class->name] = $ce; + } + } + + return $errors; + } + + /** + * Validates a single class of the current. + * + * @param ClassMetadataInfo $class + * + * @return array + */ + public function validateClass(ClassMetadataInfo $class) + { + $ce = array(); + $cmf = $this->em->getMetadataFactory(); + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if (!Type::hasType($mapping['type'])) { + $ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'."; + } + } + + foreach ($class->associationMappings as $fieldName => $assoc) { + if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) { + $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; + return $ce; + } + + if ($assoc['mappedBy'] && $assoc['inversedBy']) { + $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning."; + } + + $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']); + + if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) { + $ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " . + "the target entity '". $targetMetadata->name . "' also maps an association as identifier."; + } + + if ($assoc['mappedBy']) { + if ($targetMetadata->hasField($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association."; + } + if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist."; + } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy=".$fieldName."' attribute."; + } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". + "inconsistent with each other."; + } + } + + if ($assoc['inversedBy']) { + if ($targetMetadata->hasField($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association."; + } + + if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist."; + } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy' attribute."; + } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". + "inconsistent with each other."; + } + + // Verify inverse side/owning side match each other + if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) { + $targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']]; + if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well."; + } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many."; + } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well."; + } + } + } + + if ($assoc['isOwningSide']) { + if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $identifierColumns = $class->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$class->name."'."; + break; + } + } + + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + break; + } + } + + if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) { + $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) . + "' are missing."; + } + + if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) { + $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the source entity '". $class->name . "', " . + "however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) . + "' are missing."; + } + + } elseif ($assoc['type'] & ClassMetadataInfo::TO_ONE) { + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinColumns'] as $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + } + } + + if (count($identifierColumns) != count($assoc['joinColumns'])) { + $ids = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $ids[] = $joinColumn['name']; + } + + $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " . + "have to match to ALL identifier columns of the target entity '". $class->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) . + "' are missing."; + } + } + } + + if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) { + foreach ($assoc['orderBy'] as $orderField => $orientation) { + if (!$targetMetadata->hasField($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " . + $orderField . " that is not a field on the target entity " . $targetMetadata->name; + } + } + } + } + + foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) { + if ($publicAttr->isStatic()) { + continue; + } + + $ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ". + "or protected. Public fields may break lazy-loading."; + } + + foreach ($class->subClasses as $subClass) { + if (!in_array($class->name, class_parents($subClass))) { + $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ". + "of '" . $class->name . "' but these entities are not related through inheritance."; + } + } + + return $ce; + } + + /** + * Checks if the Database Schema is in sync with the current metadata state. + * + * @return bool + */ + public function schemaInSyncWithMetadata() + { + $schemaTool = new SchemaTool($this->em); + + $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); + + return count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php new file mode 100644 index 00000000..11992a96 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php @@ -0,0 +1,159 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\ClassLoader; +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\ORM\Configuration; +use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\Mapping\Driver\YamlDriver; + +/** + * Convenience class for setting up Doctrine from different installations and configurations. + * + * @author Benjamin Eberlei + */ +class Setup +{ + /** + * Use this method to register all autoloads for a downloaded Doctrine library. + * Pick the directory the library was uncompressed into. + * + * @param string $directory + * + * @return void + */ + public static function registerAutoloadDirectory($directory) + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $directory . "/Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine", $directory); + $loader->register(); + + $loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine"); + $loader->register(); + } + + /** + * Creates a configuration with an annotation metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @param bool $useSimpleAnnotationReader + * + * @return Configuration + */ + public static function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null, $useSimpleAnnotationReader = true) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths, $useSimpleAnnotationReader)); + + return $config; + } + + /** + * Creates a configuration with a xml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new XmlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration with a yaml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new YamlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration without a metadata driver. + * + * @param bool $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $proxyDir = $proxyDir ?: sys_get_temp_dir(); + + if ($isDevMode === false && $cache === null) { + if (extension_loaded('apc')) { + $cache = new \Doctrine\Common\Cache\ApcCache(); + } elseif (extension_loaded('xcache')) { + $cache = new \Doctrine\Common\Cache\XcacheCache(); + } elseif (extension_loaded('memcache')) { + $memcache = new \Memcache(); + $memcache->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + } elseif (extension_loaded('redis')) { + $redis = new \Redis(); + $redis->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\RedisCache(); + $cache->setRedis($redis); + } else { + $cache = new ArrayCache(); + } + } elseif ($cache === null) { + $cache = new ArrayCache(); + } + + $cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions + + $config = new Configuration(); + $config->setMetadataCacheImpl($cache); + $config->setQueryCacheImpl($cache); + $config->setResultCacheImpl($cache); + $config->setProxyDir($proxyDir); + $config->setProxyNamespace('DoctrineProxies'); + $config->setAutoGenerateProxyClasses($isDevMode); + + return $config; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php new file mode 100644 index 00000000..aebb5d8f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +class ToolEvents +{ + /** + * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata() + * whenever an entity class is transformed into its table representation. It receives + * the current non-complete Schema instance, the Entity Metadata Class instance and + * the Schema Table instance of this entity. + * + * @var string + */ + const postGenerateSchemaTable = 'postGenerateSchemaTable'; + + /** + * The postGenerateSchema event is triggered in SchemaTool#getSchemaFromMetadata() + * after all entity classes have been transformed into the related Schema structure. + * The EventArgs contain the EntityManager and the created Schema instance. + * + * @var string + */ + const postGenerateSchema = 'postGenerateSchema'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php new file mode 100644 index 00000000..0a461640 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; + +/** + * Tools related Exceptions. + * + * @author Benjamin Eberlei + */ +class ToolsException extends ORMException +{ + /** + * @param string $sql + * @param \Exception $e + * + * @return ToolsException + */ + public static function schemaToolFailure($sql, \Exception $e) + { + return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e); + } + + /** + * @param string $type + * + * @return ToolsException + */ + public static function couldNotMapDoctrine1Type($type) + { + return new self("Could not map doctrine 1 type '$type'!"); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php new file mode 100644 index 00000000..2242e60f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Is thrown when a transaction is required for the current operation, but there is none open. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class TransactionRequiredException extends ORMException +{ + /** + * @return TransactionRequiredException + */ + static public function transactionRequired() + { + return new self('An open transaction is required for this operation.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php new file mode 100644 index 00000000..3cd561f0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception for a unexpected query result. + * + * @author Fabio B. Silva + * @since 2.3 + */ +class UnexpectedResultException extends ORMException +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php new file mode 100644 index 00000000..68fde5d2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php @@ -0,0 +1,3206 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; +use InvalidArgumentException; +use UnexpectedValueException; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\NotifyPropertyChanged; +use Doctrine\Common\PropertyChangedListener; +use Doctrine\Common\Persistence\ObjectManagerAware; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Proxy\Proxy; + +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Event\PostFlushEventArgs; +use Doctrine\ORM\Event\ListenersInvoker; + +/** + * The UnitOfWork is responsible for tracking changes to objects during an + * "object-level" transaction and for writing out changes to the database + * in the correct order. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal This class contains highly performance-sensitive code. + */ +class UnitOfWork implements PropertyChangedListener +{ + /** + * An entity is in MANAGED state when its persistence is managed by an EntityManager. + */ + const STATE_MANAGED = 1; + + /** + * An entity is new if it has just been instantiated (i.e. using the "new" operator) + * and is not (yet) managed by an EntityManager. + */ + const STATE_NEW = 2; + + /** + * A detached entity is an instance with persistent state and identity that is not + * (or no longer) associated with an EntityManager (and a UnitOfWork). + */ + const STATE_DETACHED = 3; + + /** + * A removed entity instance is an instance with a persistent identity, + * associated with an EntityManager, whose persistent state will be deleted + * on commit. + */ + const STATE_REMOVED = 4; + + /** + * Hint used to collect all primary keys of associated entities during hydration + * and execute it in a dedicated query afterwards + * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql + */ + const HINT_DEFEREAGERLOAD = 'deferEagerLoad'; + + /** + * The identity map that holds references to all managed entities that have + * an identity. The entities are grouped by their class name. + * Since all classes in a hierarchy must share the same identifier set, + * we always take the root class name of the hierarchy. + * + * @var array + */ + private $identityMap = array(); + + /** + * Map of all identifiers of managed entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityIdentifiers = array(); + + /** + * Map of the original entity data of managed entities. + * Keys are object ids (spl_object_hash). This is used for calculating changesets + * at commit time. + * + * @var array + * @internal Note that PHPs "copy-on-write" behavior helps a lot with memory usage. + * A value will only really be copied if the value in the entity is modified + * by the user. + */ + private $originalEntityData = array(); + + /** + * Map of entity changes. Keys are object ids (spl_object_hash). + * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. + * + * @var array + */ + private $entityChangeSets = array(); + + /** + * The (cached) states of any known entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityStates = array(); + + /** + * Map of entities that are scheduled for dirty checking at commit time. + * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT. + * Keys are object ids (spl_object_hash). + * + * @var array + * @todo rename: scheduledForSynchronization + */ + private $scheduledForDirtyCheck = array(); + + /** + * A list of all pending entity insertions. + * + * @var array + */ + private $entityInsertions = array(); + + /** + * A list of all pending entity updates. + * + * @var array + */ + private $entityUpdates = array(); + + /** + * Any pending extra updates that have been scheduled by persisters. + * + * @var array + */ + private $extraUpdates = array(); + + /** + * A list of all pending entity deletions. + * + * @var array + */ + private $entityDeletions = array(); + + /** + * All pending collection deletions. + * + * @var array + */ + private $collectionDeletions = array(); + + /** + * All pending collection updates. + * + * @var array + */ + private $collectionUpdates = array(); + + /** + * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. + * At the end of the UnitOfWork all these collections will make new snapshots + * of their data. + * + * @var array + */ + private $visitedCollections = array(); + + /** + * The EntityManager that "owns" this UnitOfWork instance. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The calculator used to calculate the order in which changes to + * entities need to be written to the database. + * + * @var \Doctrine\ORM\Internal\CommitOrderCalculator + */ + private $commitOrderCalculator; + + /** + * The entity persister instances used to persist entity instances. + * + * @var array + */ + private $persisters = array(); + + /** + * The collection persister instances used to persist collections. + * + * @var array + */ + private $collectionPersisters = array(); + + /** + * The EventManager used for dispatching events. + * + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * The ListenersInvoker used for dispatching events. + * + * @var \Doctrine\ORM\Event\ListenersInvoker + */ + private $listenersInvoker; + + /** + * Orphaned entities that are scheduled for removal. + * + * @var array + */ + private $orphanRemovals = array(); + + /** + * Read-Only objects are never evaluated + * + * @var array + */ + private $readOnlyObjects = array(); + + /** + * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. + * + * @var array + */ + private $eagerLoadingEntities = array(); + + /** + * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->evm = $em->getEventManager(); + $this->listenersInvoker = new ListenersInvoker($em); + } + + /** + * Commits the UnitOfWork, executing all operations that have been postponed + * up to this point. The state of all managed entities will be synchronized with + * the database. + * + * The operations are executed in the following order: + * + * 1) All entity insertions + * 2) All entity updates + * 3) All collection deletions + * 4) All collection updates + * 5) All entity deletions + * + * @param null|object|array $entity + * + * @return void + * + * @throws \Exception + */ + public function commit($entity = null) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em)); + } + + // Compute changes done since last commit. + if ($entity === null) { + $this->computeChangeSets(); + } elseif (is_object($entity)) { + $this->computeSingleEntityChangeSet($entity); + } elseif (is_array($entity)) { + foreach ($entity as $object) { + $this->computeSingleEntityChangeSet($object); + } + } + + if ( ! ($this->entityInsertions || + $this->entityDeletions || + $this->entityUpdates || + $this->collectionUpdates || + $this->collectionDeletions || + $this->orphanRemovals)) { + $this->dispatchOnFlushEvent(); + $this->dispatchPostFlushEvent(); + + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $orphan) { + $this->remove($orphan); + } + } + + $this->dispatchOnFlushEvent(); + + // Now we need a commit order to maintain referential integrity + $commitOrder = $this->getCommitOrder(); + + $conn = $this->em->getConnection(); + $conn->beginTransaction(); + + try { + if ($this->entityInsertions) { + foreach ($commitOrder as $class) { + $this->executeInserts($class); + } + } + + if ($this->entityUpdates) { + foreach ($commitOrder as $class) { + $this->executeUpdates($class); + } + } + + // Extra updates that were requested by persisters. + if ($this->extraUpdates) { + $this->executeExtraUpdates(); + } + + // Collection deletions (deletions of complete collections) + foreach ($this->collectionDeletions as $collectionToDelete) { + $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete); + } + // Collection updates (deleteRows, updateRows, insertRows) + foreach ($this->collectionUpdates as $collectionToUpdate) { + $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate); + } + + // Entity deletions come last and need to be in reverse commit order + if ($this->entityDeletions) { + for ($count = count($commitOrder), $i = $count - 1; $i >= 0; --$i) { + $this->executeDeletions($commitOrder[$i]); + } + } + + $conn->commit(); + } catch (Exception $e) { + $this->em->close(); + $conn->rollback(); + + throw $e; + } + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + $this->dispatchPostFlushEvent(); + + // Clear up + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->entityChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForDirtyCheck = + $this->orphanRemovals = array(); + } + + /** + * Computes the changesets of all entities scheduled for insertion. + * + * @return void + */ + private function computeScheduleInsertsChangeSets() + { + foreach ($this->entityInsertions as $entity) { + $class = $this->em->getClassMetadata(get_class($entity)); + + $this->computeChangeSet($class, $entity); + } + } + + /** + * Only flushes the given entity according to a ruleset that keeps the UoW consistent. + * + * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well! + * 2. Read Only entities are skipped. + * 3. Proxies are skipped. + * 4. Only if entity is properly managed. + * + * @param object $entity + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function computeSingleEntityChangeSet($entity) + { + $state = $this->getEntityState($entity); + + if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) { + throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity)); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) { + $this->persist($entity); + } + + // Compute changes for INSERTed entities first. This must always happen even in this case. + $this->computeScheduleInsertsChangeSets(); + + if ($class->isReadOnly) { + return; + } + + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + return; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + + /** + * Executes any extra updates that have been scheduled. + */ + private function executeExtraUpdates() + { + foreach ($this->extraUpdates as $oid => $update) { + list ($entity, $changeset) = $update; + + $this->entityChangeSets[$oid] = $changeset; + $this->getEntityPersister(get_class($entity))->update($entity); + } + } + + /** + * Gets the changeset for an entity. + * + * @param object $entity + * + * @return array + */ + public function getEntityChangeSet($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityChangeSets[$oid])) { + return $this->entityChangeSets[$oid]; + } + + return array(); + } + + /** + * Computes the changes that happened to a single entity. + * + * Modifies/populates the following properties: + * + * {@link _originalEntityData} + * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) + * then it was not fetched from the database and therefore we have no original + * entity data yet. All of the current entity data is stored as the original entity data. + * + * {@link _entityChangeSets} + * The changes detected on all properties of the entity are stored there. + * A change is a tuple array where the first entry is the old value and the second + * entry is the new value of the property. Changesets are used by persisters + * to INSERT/UPDATE the persistent entity state. + * + * {@link _entityUpdates} + * If the entity is already fully MANAGED (has been fetched from the database before) + * and any changes to its properties are detected, then a reference to the entity is stored + * there to mark it for an update. + * + * {@link _collectionDeletions} + * If a PersistentCollection has been de-referenced in a fully MANAGED entity, + * then this collection is marked for deletion. + * + * @ignore + * + * @internal Don't call from the outside. + * + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to compute the changes. + * + * @return void + */ + public function computeChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->readOnlyObjects[$oid])) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + $value = $refProp->getValue($entity); + + if ($class->isCollectionValuedAssociation($name) && $value !== null && ! ($value instanceof PersistentCollection)) { + // If $value is not a Collection then use an ArrayCollection. + if ( ! $value instanceof Collection) { + $value = new ArrayCollection($value); + } + + $assoc = $class->associationMappings[$name]; + + // Inject PersistentCollection + $value = new PersistentCollection( + $this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value + ); + $value->setOwner($entity, $assoc); + $value->setDirty( ! $value->isEmpty()); + + $class->reflFields[$name]->setValue($entity, $value); + + $actualData[$name] = $value; + + continue; + } + + if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) { + $actualData[$name] = $value; + } + } + + if ( ! isset($this->originalEntityData[$oid])) { + // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). + // These result in an INSERT. + $this->originalEntityData[$oid] = $actualData; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + if ( ! isset($class->associationMappings[$propName])) { + $changeSet[$propName] = array(null, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + $changeSet[$propName] = array(null, $actualValue); + } + } + + $this->entityChangeSets[$oid] = $changeSet; + } else { + // Entity is "fully" MANAGED: it was already fully persisted before + // and we have a copy of the original data + $originalData = $this->originalEntityData[$oid]; + $isChangeTrackingNotify = $class->isChangeTrackingNotify(); + $changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) + ? $this->entityChangeSets[$oid] + : array(); + + foreach ($actualData as $propName => $actualValue) { + // skip field, its a partially omitted one! + if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { + continue; + } + + $orgValue = $originalData[$propName]; + + // skip if value haven't changed + if ($orgValue === $actualValue) { + continue; + } + + // if regular field + if ( ! isset($class->associationMappings[$propName])) { + if ($isChangeTrackingNotify) { + continue; + } + + $changeSet[$propName] = array($orgValue, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + // Persistent collection was exchanged with the "originally" + // created one. This can only mean it was cloned and replaced + // on another entity. + if ($actualValue instanceof PersistentCollection) { + $owner = $actualValue->getOwner(); + if ($owner === null) { // cloned + $actualValue->setOwner($entity, $assoc); + } else if ($owner !== $entity) { // no clone, we have to fix + if (!$actualValue->isInitialized()) { + $actualValue->initialize(); // we have to do this otherwise the cols share state + } + $newValue = clone $actualValue; + $newValue->setOwner($entity, $assoc); + $class->reflFields[$propName]->setValue($entity, $newValue); + } + } + + if ($orgValue instanceof PersistentCollection) { + // A PersistentCollection was de-referenced, so delete it. + $coid = spl_object_hash($orgValue); + + if (isset($this->collectionDeletions[$coid])) { + continue; + } + + $this->collectionDeletions[$coid] = $orgValue; + $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. + + continue; + } + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc['isOwningSide']) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + + if ($orgValue !== null && $assoc['orphanRemoval']) { + $this->scheduleOrphanRemoval($orgValue); + } + } + } + + if ($changeSet) { + $this->entityChangeSets[$oid] = $changeSet; + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + + // Look for changes in associations of the entity + foreach ($class->associationMappings as $field => $assoc) { + if (($val = $class->reflFields[$field]->getValue($entity)) !== null) { + $this->computeAssociationChanges($assoc, $val); + if (!isset($this->entityChangeSets[$oid]) && + $assoc['isOwningSide'] && + $assoc['type'] == ClassMetadata::MANY_TO_MANY && + $val instanceof PersistentCollection && + $val->isDirty()) { + $this->entityChangeSets[$oid] = array(); + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + } + } + + /** + * Computes all the changes that have been done to entities and collections + * since the last commit and stores these changes in the _entityChangeSet map + * temporarily for access by the persisters, until the UoW commit is finished. + * + * @return void + */ + public function computeChangeSets() + { + // Compute changes for INSERTed entities first. This must always happen. + $this->computeScheduleInsertsChangeSets(); + + // Compute changes for other MANAGED entities. Change tracking policies take effect here. + foreach ($this->identityMap as $className => $entities) { + $class = $this->em->getClassMetadata($className); + + // Skip class if instances are read-only + if ($class->isReadOnly) { + continue; + } + + // If change tracking is explicit or happens through notification, then only compute + // changes on entities of that type that are explicitly marked for synchronization. + switch (true) { + case ($class->isChangeTrackingDeferredImplicit()): + $entitiesToProcess = $entities; + break; + + case (isset($this->scheduledForDirtyCheck[$className])): + $entitiesToProcess = $this->scheduledForDirtyCheck[$className]; + break; + + default: + $entitiesToProcess = array(); + + } + + foreach ($entitiesToProcess as $entity) { + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + continue; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + } + } + + /** + * Computes the changes of an association. + * + * @param array $assoc + * @param mixed $value The value of the association. + * + * @throws ORMInvalidArgumentException + * @throws ORMException + * + * @return void + */ + private function computeAssociationChanges($assoc, $value) + { + if ($value instanceof Proxy && ! $value->__isInitialized__) { + return; + } + + if ($value instanceof PersistentCollection && $value->isDirty()) { + $coid = spl_object_hash($value); + + if ($assoc['isOwningSide']) { + $this->collectionUpdates[$coid] = $value; + } + + $this->visitedCollections[$coid] = $value; + } + + // Look through the entities, and in any of their associations, + // for transient (new) entities, recursively. ("Persistence by reachability") + // Unwrap. Uninitialized collections will simply be empty. + $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($unwrappedValue as $key => $entry) { + $state = $this->getEntityState($entry, self::STATE_NEW); + + if ( ! ($entry instanceof $assoc['targetEntity'])) { + throw new ORMException( + sprintf( + 'Found entity of type %s on association %s#%s, but expecting %s', + get_class($entry), + $assoc['sourceEntity'], + $assoc['fieldName'], + $targetClass->name + ) + ); + } + + switch ($state) { + case self::STATE_NEW: + if ( ! $assoc['isCascadePersist']) { + throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry); + } + + $this->persistNew($targetClass, $entry); + $this->computeChangeSet($targetClass, $entry); + break; + + case self::STATE_REMOVED: + // Consume the $value as array (it's either an array or an ArrayAccess) + // and remove the element from Collection. + if ($assoc['type'] & ClassMetadata::TO_MANY) { + unset($value[$key]); + } + break; + + case self::STATE_DETACHED: + // Can actually not happen right now as we assume STATE_NEW, + // so the exception will be raised from the DBAL layer (constraint violation). + throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); + break; + + default: + // MANAGED associated entities are already taken into account + // during changeset calculation anyway, since they are in the identity map. + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param object $entity + * + * @return void + */ + private function persistNew($class, $entity) + { + $oid = spl_object_hash($entity); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $idGen = $class->idGenerator; + + if ( ! $idGen->isPostInsertGenerator()) { + $idValue = $idGen->generate($this->em, $entity); + + if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { + $idValue = array($class->identifier[0] => $idValue); + + $class->setIdentifierValues($entity, $idValue); + } + + $this->entityIdentifiers[$oid] = $idValue; + } + + $this->entityStates[$oid] = self::STATE_MANAGED; + + $this->scheduleForInsert($entity); + } + + /** + * INTERNAL: + * Computes the changeset of an individual entity, independently of the + * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). + * + * The passed entity must be a managed entity. If the entity already has a change set + * because this method is invoked during a commit cycle then the change sets are added. + * whereby changes detected in this method prevail. + * + * @ignore + * + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to (re)calculate the change set. + * + * @return void + * + * @throws ORMInvalidArgumentException If the passed entity is not MANAGED. + */ + public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + // skip if change tracking is "NOTIFY" + if ($class->isChangeTrackingNotify()) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) { + $actualData[$name] = $refProp->getValue($entity); + } + } + + $originalData = $this->originalEntityData[$oid]; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + if (is_object($orgValue) && $orgValue !== $actualValue) { + $changeSet[$propName] = array($orgValue, $actualValue); + } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + } + + if ($changeSet) { + if (isset($this->entityChangeSets[$oid])) { + $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + } + + $this->originalEntityData[$oid] = $actualData; + } + } + + /** + * Executes all entity insertions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeInserts($class) + { + $entities = array(); + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); + + foreach ($this->entityInsertions as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + $persister->addInsert($entity); + + unset($this->entityInsertions[$oid]); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $entities[] = $entity; + } + } + + $postInsertIds = $persister->executeInserts(); + + if ($postInsertIds) { + // Persister returned post-insert IDs + foreach ($postInsertIds as $id => $entity) { + $oid = spl_object_hash($entity); + $idField = $class->identifier[0]; + + $class->reflFields[$idField]->setValue($entity, $id); + + $this->entityIdentifiers[$oid] = array($idField => $id); + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid][$idField] = $id; + + $this->addToIdentityMap($entity); + } + } + + foreach ($entities as $entity) { + $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + + /** + * Executes all entity updates for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeUpdates($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate); + $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate); + + foreach ($this->entityUpdates as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]), $preUpdateInvoke); + $this->recomputeSingleEntityChangeSet($class, $entity); + } + + if ( ! empty($this->entityChangeSets[$oid])) { + $persister->update($entity); + } + + unset($this->entityUpdates[$oid]); + + if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke); + } + } + } + + /** + * Executes all entity deletions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeDeletions($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove); + + foreach ($this->entityDeletions as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + $persister->delete($entity); + + unset( + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->originalEntityData[$oid], + $this->entityStates[$oid] + ); + + // Entity with this $oid after deletion treated as NEW, even if the $oid + // is obtained by a new entity because the old one went out of scope. + //$this->entityStates[$oid] = self::STATE_NEW; + if ( ! $class->isIdentifierNatural()) { + $class->reflFields[$class->identifier[0]]->setValue($entity, null); + } + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + } + + /** + * Gets the commit order. + * + * @param array|null $entityChangeSet + * + * @return array + */ + private function getCommitOrder(array $entityChangeSet = null) + { + if ($entityChangeSet === null) { + $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions); + } + + $calc = $this->getCommitOrderCalculator(); + + // See if there are any new classes in the changeset, that are not in the + // commit order graph yet (don't have a node). + // We have to inspect changeSet to be able to correctly build dependencies. + // It is not possible to use IdentityMap here because post inserted ids + // are not yet available. + $newNodes = array(); + + foreach ($entityChangeSet as $entity) { + $className = $this->em->getClassMetadata(get_class($entity))->name; + + if ($calc->hasClass($className)) { + continue; + } + + $class = $this->em->getClassMetadata($className); + $calc->addClass($class); + + $newNodes[] = $class; + } + + // Calculate dependencies for new nodes + while ($class = array_pop($newNodes)) { + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ( ! $calc->hasClass($targetClass->name)) { + $calc->addClass($targetClass); + + $newNodes[] = $targetClass; + } + + $calc->addDependency($targetClass, $class); + + // If the target class has mapped subclasses, these share the same dependency. + if ( ! $targetClass->subClasses) { + continue; + } + + foreach ($targetClass->subClasses as $subClassName) { + $targetSubClass = $this->em->getClassMetadata($subClassName); + + if ( ! $calc->hasClass($subClassName)) { + $calc->addClass($targetSubClass); + + $newNodes[] = $targetSubClass; + } + + $calc->addDependency($targetSubClass, $class); + } + } + } + + return $calc->getCommitOrder(); + } + + /** + * Schedules an entity for insertion into the database. + * If the entity already has an identifier, it will be added to the identity map. + * + * @param object $entity The entity to schedule for insertion. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws \InvalidArgumentException + */ + public function scheduleForInsert($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityUpdates[$oid])) { + throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion."); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity); + } + if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity); + } + + if (isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertTwice($entity); + } + + $this->entityInsertions[$oid] = $entity; + + if (isset($this->entityIdentifiers[$oid])) { + $this->addToIdentityMap($entity); + } + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * Checks whether an entity is scheduled for insertion. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForInsert($entity) + { + return isset($this->entityInsertions[spl_object_hash($entity)]); + } + + /** + * Schedules an entity for being updated. + * + * @param object $entity The entity to schedule for being updated. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function scheduleForUpdate($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update"); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update"); + } + + if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) { + $this->entityUpdates[$oid] = $entity; + } + } + + /** + * INTERNAL: + * Schedules an extra update that will be executed immediately after the + * regular entity updates within the currently running commit cycle. + * + * Extra updates for entities are stored as (entity, changeset) tuples. + * + * @ignore + * + * @param object $entity The entity for which to schedule an extra update. + * @param array $changeset The changeset of the entity (what to update). + * + * @return void + */ + public function scheduleExtraUpdate($entity, array $changeset) + { + $oid = spl_object_hash($entity); + $extraUpdate = array($entity, $changeset); + + if (isset($this->extraUpdates[$oid])) { + list($ignored, $changeset2) = $this->extraUpdates[$oid]; + + $extraUpdate = array($entity, $changeset + $changeset2); + } + + $this->extraUpdates[$oid] = $extraUpdate; + } + + /** + * Checks whether an entity is registered as dirty in the unit of work. + * Note: Is not very useful currently as dirty entities are only registered + * at commit time. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForUpdate($entity) + { + return isset($this->entityUpdates[spl_object_hash($entity)]); + } + + /** + * Checks whether an entity is registered to be checked in the unit of work. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForDirtyCheck($entity) + { + $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + return isset($this->scheduledForDirtyCheck[$rootEntityName][spl_object_hash($entity)]); + } + + /** + * INTERNAL: + * Schedules an entity for deletion. + * + * @param object $entity + * + * @return void + */ + public function scheduleForDelete($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityInsertions[$oid])) { + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset($this->entityInsertions[$oid], $this->entityStates[$oid]); + + return; // entity has not been persisted yet, so nothing more to do. + } + + if ( ! $this->isInIdentityMap($entity)) { + return; + } + + $this->removeFromIdentityMap($entity); + + if (isset($this->entityUpdates[$oid])) { + unset($this->entityUpdates[$oid]); + } + + if ( ! isset($this->entityDeletions[$oid])) { + $this->entityDeletions[$oid] = $entity; + $this->entityStates[$oid] = self::STATE_REMOVED; + } + } + + /** + * Checks whether an entity is registered as removed/deleted with the unit + * of work. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForDelete($entity) + { + return isset($this->entityDeletions[spl_object_hash($entity)]); + } + + /** + * Checks whether an entity is scheduled for insertion, update or deletion. + * + * @param object $entity + * + * @return boolean + */ + public function isEntityScheduled($entity) + { + $oid = spl_object_hash($entity); + + return isset($this->entityInsertions[$oid]) + || isset($this->entityUpdates[$oid]) + || isset($this->entityDeletions[$oid]); + } + + /** + * INTERNAL: + * Registers an entity in the identity map. + * Note that entities in a hierarchy are registered with the class name of + * the root entity. + * + * @ignore + * + * @param object $entity The entity to register. + * + * @return boolean TRUE if the registration was successful, FALSE if the identity of + * the entity in question is already managed. + * + * @throws ORMInvalidArgumentException + */ + public function addToIdentityMap($entity) + { + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + return false; + } + + $this->identityMap[$className][$idHash] = $entity; + + return true; + } + + /** + * Gets the state of an entity with regard to the current unit of work. + * + * @param object $entity + * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED). + * This parameter can be set to improve performance of entity state detection + * by potentially avoiding a database lookup if the distinction between NEW and DETACHED + * is either known or does not matter for the caller of the method. + * + * @return int The entity state. + */ + public function getEntityState($entity, $assume = null) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityStates[$oid])) { + return $this->entityStates[$oid]; + } + + if ($assume !== null) { + return $assume; + } + + // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known. + // Note that you can not remember the NEW or DETACHED state in _entityStates since + // the UoW does not hold references to such objects and the object hash can be reused. + // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it. + $class = $this->em->getClassMetadata(get_class($entity)); + $id = $class->getIdentifierValues($entity); + + if ( ! $id) { + return self::STATE_NEW; + } + + if ($class->containsForeignIdentifier) { + $id = $this->flattenIdentifier($class, $id); + } + + switch (true) { + case ($class->isIdentifierNatural()); + // Check for a version field, if available, to avoid a db lookup. + if ($class->isVersioned) { + return ($class->getFieldValue($entity, $class->versionField)) + ? self::STATE_DETACHED + : self::STATE_NEW; + } + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister($class->name)->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + case ( ! $class->idGenerator->isPostInsertGenerator()): + // if we have a pre insert generator we can't be sure that having an id + // really means that the entity exists. We have to verify this through + // the last resort: a db lookup + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister($class->name)->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + default: + return self::STATE_DETACHED; + } + } + + /** + * INTERNAL: + * Removes an entity from the identity map. This effectively detaches the + * entity from the persistence management of Doctrine. + * + * @ignore + * + * @param object $entity + * + * @return boolean + * + * @throws ORMInvalidArgumentException + */ + public function removeFromIdentityMap($entity) + { + $oid = spl_object_hash($entity); + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map"); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + unset($this->identityMap[$className][$idHash]); + unset($this->readOnlyObjects[$oid]); + + //$this->entityStates[$oid] = self::STATE_DETACHED; + + return true; + } + + return false; + } + + /** + * INTERNAL: + * Gets an entity in the identity map by its identifier hash. + * + * @ignore + * + * @param string $idHash + * @param string $rootClassName + * + * @return object + */ + public function getByIdHash($idHash, $rootClassName) + { + return $this->identityMap[$rootClassName][$idHash]; + } + + /** + * INTERNAL: + * Tries to get an entity by its identifier hash. If no entity is found for + * the given hash, FALSE is returned. + * + * @ignore + * + * @param string $idHash + * @param string $rootClassName + * + * @return object|bool The found entity or FALSE. + */ + public function tryGetByIdHash($idHash, $rootClassName) + { + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + return false; + } + + /** + * Checks whether an entity is registered in the identity map of this UnitOfWork. + * + * @param object $entity + * + * @return boolean + */ + public function isInIdentityMap($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + return false; + } + + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + return false; + } + + return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]); + } + + /** + * INTERNAL: + * Checks whether an identifier hash exists in the identity map. + * + * @ignore + * + * @param string $idHash + * @param string $rootClassName + * + * @return boolean + */ + public function containsIdHash($idHash, $rootClassName) + { + return isset($this->identityMap[$rootClassName][$idHash]); + } + + /** + * Persists an entity as part of the current unit of work. + * + * @param object $entity The entity to persist. + * + * @return void + */ + public function persist($entity) + { + $visited = array(); + + $this->doPersist($entity, $visited); + } + + /** + * Persists an entity as part of the current unit of work. + * + * This method is internally called during persist() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to persist. + * @param array $visited The already visited entities. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws UnexpectedValueException + */ + private function doPersist($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // Mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation). + // If we would detect DETACHED here we would throw an exception anyway with the same + // consequences (not recoverable/programming error), so just assuming NEW here + // lets us avoid some database lookups for entities with natural identifiers. + $entityState = $this->getEntityState($entity, self::STATE_NEW); + + switch ($entityState) { + case self::STATE_MANAGED: + // Nothing to do, except if policy is "deferred explicit" + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + break; + + case self::STATE_NEW: + $this->persistNew($class, $entity); + break; + + case self::STATE_REMOVED: + // Entity becomes managed again + unset($this->entityDeletions[$oid]); + + $this->entityStates[$oid] = self::STATE_MANAGED; + break; + + case self::STATE_DETACHED: + // Can actually not happen right now since we assume STATE_NEW. + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted"); + + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + $this->cascadePersist($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * @param object $entity The entity to remove. + * + * @return void + */ + public function remove($entity) + { + $visited = array(); + + $this->doRemove($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * This method is internally called during delete() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to delete. + * @param array $visited The map of the already visited entities. + * + * @return void + * + * @throws ORMInvalidArgumentException If the instance is a detached entity. + * @throws UnexpectedValueException + */ + private function doRemove($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + // Cascade first, because scheduleForDelete() removes the entity from the identity map, which + // can cause problems when a lazy proxy has to be initialized for the cascade operation. + $this->cascadeRemove($entity, $visited); + + $class = $this->em->getClassMetadata(get_class($entity)); + $entityState = $this->getEntityState($entity); + + switch ($entityState) { + case self::STATE_NEW: + case self::STATE_REMOVED: + // nothing to do + break; + + case self::STATE_MANAGED: + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $this->scheduleForDelete($entity); + break; + + case self::STATE_DETACHED: + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed"); + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + } + + /** + * Merges the state of the given detached entity into this UnitOfWork. + * + * @param object $entity + * + * @return object The managed copy of the entity. + * + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * + * @todo Require active transaction!? OptimisticLockException may result in undefined state!? + */ + public function merge($entity) + { + $visited = array(); + + return $this->doMerge($entity, $visited); + } + + /** + * convert foreign identifiers into scalar foreign key values to avoid object to string conversion failures. + * + * @param ClassMetadata $class + * @param array $id + * @return array + */ + private function flattenIdentifier($class, $id) + { + $flatId = array(); + + foreach ($id as $idField => $idValue) { + if (isset($class->associationMappings[$idField])) { + $targetClassMetadata = $this->em->getClassMetadata($class->associationMappings[$idField]['targetEntity']); + $associatedId = $this->getEntityIdentifier($idValue); + + $flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]]; + } + } + + return $flatId; + } + + /** + * Executes a merge operation on an entity. + * + * @param object $entity + * @param array $visited + * @param object|null $prevManagedCopy + * @param array|null $assoc + * + * @return object The managed copy of the entity. + * + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * @throws ORMInvalidArgumentException If the entity instance is NEW. + * @throws EntityNotFoundException + */ + private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return $visited[$oid]; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + // First we assume DETACHED, although it can still be NEW but we can avoid + // an extra db-roundtrip this way. If it is not MANAGED but has an identity, + // we need to fetch it from the db anyway in order to merge. + // MANAGED entities are ignored by the merge operation. + $managedCopy = $entity; + + if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { + if ($entity instanceof Proxy && ! $entity->__isInitialized()) { + $this->em->getProxyFactory()->resetUninitializedProxy($entity); + $entity->__load(); + } + + // Try to look the entity up in the identity map. + $id = $class->getIdentifierValues($entity); + + // If there is no ID, it is actually NEW. + if ( ! $id) { + $managedCopy = $this->newInstance($class); + + $this->persistNew($class, $managedCopy); + } else { + $flatId = ($class->containsForeignIdentifier) + ? $this->flattenIdentifier($class, $id) + : $id; + + $managedCopy = $this->tryGetById($flatId, $class->rootEntityName); + + if ($managedCopy) { + // We have the entity in-memory already, just make sure its not removed. + if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) { + throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge"); + } + } else { + // We need to fetch the managed copy in order to merge. + $managedCopy = $this->em->find($class->name, $flatId); + } + + if ($managedCopy === null) { + // If the identifier is ASSIGNED, it is NEW, otherwise an error + // since the managed entity was not found. + if ( ! $class->isIdentifierNatural()) { + throw new EntityNotFoundException; + } + + $managedCopy = $this->newInstance($class); + $class->setIdentifierValues($managedCopy, $id); + + $this->persistNew($class, $managedCopy); + } else { + if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized__) { + $managedCopy->__load(); + } + } + } + + if ($class->isVersioned) { + $reflField = $class->reflFields[$class->versionField]; + $managedCopyVersion = $reflField->getValue($managedCopy); + $entityVersion = $reflField->getValue($entity); + + // Throw exception if versions don't match. + if ($managedCopyVersion != $entityVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); + } + } + + // Merge state of $entity into existing (managed) entity + foreach ($class->reflClass->getProperties() as $prop) { + $name = $prop->name; + $prop->setAccessible(true); + if ( ! isset($class->associationMappings[$name])) { + if ( ! $class->isIdentifier($name)) { + $prop->setValue($managedCopy, $prop->getValue($entity)); + } + } else { + $assoc2 = $class->associationMappings[$name]; + if ($assoc2['type'] & ClassMetadata::TO_ONE) { + $other = $prop->getValue($entity); + if ($other === null) { + $prop->setValue($managedCopy, null); + } else if ($other instanceof Proxy && !$other->__isInitialized__) { + // do not merge fields marked lazy that have not been fetched. + continue; + } else if ( ! $assoc2['isCascadeMerge']) { + if ($this->getEntityState($other) === self::STATE_DETACHED) { + $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); + $relatedId = $targetClass->getIdentifierValues($other); + + if ($targetClass->subClasses) { + $other = $this->em->find($targetClass->name, $relatedId); + } else { + $other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId); + $this->registerManaged($other, $relatedId, array()); + } + } + + $prop->setValue($managedCopy, $other); + } + } else { + $mergeCol = $prop->getValue($entity); + if ($mergeCol instanceof PersistentCollection && !$mergeCol->isInitialized()) { + // do not merge fields marked lazy that have not been fetched. + // keep the lazy persistent collection of the managed copy. + continue; + } + + $managedCol = $prop->getValue($managedCopy); + if (!$managedCol) { + $managedCol = new PersistentCollection($this->em, + $this->em->getClassMetadata($assoc2['targetEntity']), + new ArrayCollection + ); + $managedCol->setOwner($managedCopy, $assoc2); + $prop->setValue($managedCopy, $managedCol); + $this->originalEntityData[$oid][$name] = $managedCol; + } + if ($assoc2['isCascadeMerge']) { + $managedCol->initialize(); + + // clear and set dirty a managed collection if its not also the same collection to merge from. + if (!$managedCol->isEmpty() && $managedCol !== $mergeCol) { + $managedCol->unwrap()->clear(); + $managedCol->setDirty(true); + + if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) { + $this->scheduleForDirtyCheck($managedCopy); + } + } + } + } + } + + if ($class->isChangeTrackingNotify()) { + // Just treat all properties as changed, there is no other choice. + $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); + } + } + + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + } + + if ($prevManagedCopy !== null) { + $assocField = $assoc['fieldName']; + $prevClass = $this->em->getClassMetadata(get_class($prevManagedCopy)); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); + } else { + $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy); + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy); + } + } + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = true; + + $this->cascadeMerge($entity, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Detaches an entity from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $entity The entity to detach. + * + * @return void + */ + public function detach($entity) + { + $visited = array(); + + $this->doDetach($entity, $visited); + } + + /** + * Executes a detach operation on the given entity. + * + * @param object $entity + * @param array $visited + * @param boolean $noCascade if true, don't cascade detach operation. + * + * @return void + */ + private function doDetach($entity, array &$visited, $noCascade = false) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + switch ($this->getEntityState($entity, self::STATE_DETACHED)) { + case self::STATE_MANAGED: + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset( + $this->entityInsertions[$oid], + $this->entityUpdates[$oid], + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->entityStates[$oid], + $this->originalEntityData[$oid] + ); + break; + case self::STATE_NEW: + case self::STATE_DETACHED: + return; + } + + if ( ! $noCascade) { + $this->cascadeDetach($entity, $visited); + } + } + + /** + * Refreshes the state of the given entity from the database, overwriting + * any local, unpersisted changes. + * + * @param object $entity The entity to refresh. + * + * @return void + * + * @throws InvalidArgumentException If the entity is not MANAGED. + */ + public function refresh($entity) + { + $visited = array(); + + $this->doRefresh($entity, $visited); + } + + /** + * Executes a refresh operation on an entity. + * + * @param object $entity The entity to refresh. + * @param array $visited The already visited entities during cascades. + * + * @return void + * + * @throws ORMInvalidArgumentException If the entity is not MANAGED. + */ + private function doRefresh($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($this->getEntityState($entity) !== self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $this->getEntityPersister($class->name)->refresh( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $entity + ); + + $this->cascadeRefresh($entity, $visited); + } + + /** + * Cascades a refresh operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeRefresh($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRefresh']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doRefresh($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRefresh($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a detach operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeDetach($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeDetach']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doDetach($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doDetach($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a merge operation to associated entities. + * + * @param object $entity + * @param object $managedCopy + * @param array $visited + * + * @return void + */ + private function cascadeMerge($entity, $managedCopy, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeMerge']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + if ($relatedEntities instanceof Collection) { + if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { + continue; + } + + if ($relatedEntities instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + } + + foreach ($relatedEntities as $relatedEntity) { + $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); + } + } else if ($relatedEntities !== null) { + $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc); + } + } + } + + /** + * Cascades the save operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadePersist($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadePersist']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doPersist($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doPersist($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades the delete operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeRemove($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + + foreach ($associationMappings as $assoc) { + if ($entity instanceof Proxy && !$entity->__isInitialized__) { + $entity->__load(); + } + + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + // If its a PersistentCollection initialization is intended! No unwrap! + foreach ($relatedEntities as $relatedEntity) { + $this->doRemove($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRemove($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws OptimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + if ($entity === null) { + throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock()."); + } + + if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + switch ($lockMode) { + case \Doctrine\DBAL\LockMode::OPTIMISTIC; + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + if ($lockVersion === null) { + return; + } + + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + if ($entityVersion != $lockVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion); + } + + break; + + case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ: + case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE: + if (!$this->em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + $oid = spl_object_hash($entity); + + $this->getEntityPersister($class->name)->lock( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $lockMode + ); + break; + + default: + // Do nothing + } + } + + /** + * Gets the CommitOrderCalculator used by the UnitOfWork to order commits. + * + * @return \Doctrine\ORM\Internal\CommitOrderCalculator + */ + public function getCommitOrderCalculator() + { + if ($this->commitOrderCalculator === null) { + $this->commitOrderCalculator = new Internal\CommitOrderCalculator; + } + + return $this->commitOrderCalculator; + } + + /** + * Clears the UnitOfWork. + * + * @param string|null $entityName if given, only entities of this type will get detached. + * + * @return void + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForDirtyCheck = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + foreach ($this->identityMap as $className => $entities) { + if ($className === $entityName) { + foreach ($entities as $entity) { + $this->doDetach($entity, $visited, true); + } + } + } + } + + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); + } + } + + /** + * INTERNAL: + * Schedules an orphaned entity for removal. The remove() operation will be + * invoked on that entity at the beginning of the next commit of this + * UnitOfWork. + * + * @ignore + * + * @param object $entity + * + * @return void + */ + public function scheduleOrphanRemoval($entity) + { + $this->orphanRemovals[spl_object_hash($entity)] = $entity; + } + + /** + * INTERNAL: + * Schedules a complete collection for removal when this UnitOfWork commits. + * + * @param PersistentCollection $coll + * + * @return void + */ + public function scheduleCollectionDeletion(PersistentCollection $coll) + { + $coid = spl_object_hash($coll); + + //TODO: if $coll is already scheduled for recreation ... what to do? + // Just remove $coll from the scheduled recreations? + if (isset($this->collectionUpdates[$coid])) { + unset($this->collectionUpdates[$coid]); + } + + $this->collectionDeletions[$coid] = $coll; + } + + /** + * @param PersistentCollection $coll + * + * @return bool + */ + public function isCollectionScheduledForDeletion(PersistentCollection $coll) + { + return isset($this->collectionDeletions[spl_object_hash($coll)]); + } + + /** + * @param ClassMetadata $class + * + * @return \Doctrine\Common\Persistence\ObjectManagerAware|object + */ + private function newInstance($class) + { + $entity = $class->newInstance(); + + if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + return $entity; + } + + /** + * INTERNAL: + * Creates an entity. Used for reconstitution of persistent entities. + * + * @ignore + * + * @param string $className The name of the entity class. + * @param array $data The data for the entity. + * @param array $hints Any hints to account for during reconstitution/lookup of the entity. + * + * @return object The managed entity instance. + * + * @internal Highly performance-sensitive method. + * + * @todo Rename: getOrCreateEntity + */ + public function createEntity($className, array $data, &$hints = array()) + { + $class = $this->em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); + + if ($class->isIdentifierComposite) { + $id = array(); + + foreach ($class->identifier as $fieldName) { + $id[$fieldName] = isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]; + } + + $idHash = implode(' ', $id); + } else { + $idHash = isset($class->associationMappings[$class->identifier[0]]) + ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']] + : $data[$class->identifier[0]]; + + $id = array($class->identifier[0] => $idHash); + } + + if (isset($this->identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->identityMap[$class->rootEntityName][$idHash]; + $oid = spl_object_hash($entity); + + if ( + isset($hints[Query::HINT_REFRESH]) + && isset($hints[Query::HINT_REFRESH_ENTITY]) + && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity + && $unmanagedProxy instanceof Proxy + && (($unmanagedProxyClass = $this->em->getClassMetadata(get_class($unmanagedProxy))) === $class) + ) { + // DDC-1238 - we have a managed instance, but it isn't the provided one. + // Therefore we clear its identifier. Also, we must re-fetch metadata since the + // refreshed object may be anything + + foreach ($unmanagedProxyClass->identifier as $fieldName) { + $unmanagedProxyClass->reflFields[$fieldName]->setValue($unmanagedProxy, null); + } + + return $unmanagedProxy; + } + + if ($entity instanceof Proxy && ! $entity->__isInitialized()) { + $entity->__setInitialized(true); + + $overrideLocalValues = true; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + // inject ObjectManager into just loaded proxies. + if ($overrideLocalValues && $entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + } else { + $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + + // If only a specific entity is set to refresh, check that it's the one + if(isset($hints[Query::HINT_REFRESH_ENTITY])) { + $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity; + } + + // inject ObjectManager upon refresh. + if ($overrideLocalValues && $entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + } + + if ($overrideLocalValues) { + $this->originalEntityData[$oid] = $data; + } + } else { + $entity = $this->newInstance($class); + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + + $this->identityMap[$class->rootEntityName][$idHash] = $entity; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + $overrideLocalValues = true; + } + + if ( ! $overrideLocalValues) { + return $entity; + } + + foreach ($data as $field => $value) { + if (isset($class->fieldMappings[$field])) { + $class->reflFields[$field]->setValue($entity, $value); + } + } + + // Loading the entity right here, if its in the eager loading map get rid of it there. + unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]); + + if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) { + unset($this->eagerLoadingEntities[$class->rootEntityName]); + } + + // Properly initialize any unfetched associations, if partial objects are not allowed. + if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { + return $entity; + } + + foreach ($class->associationMappings as $field => $assoc) { + // Check if the association is not among the fetch-joined associations already. + if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + if ( ! $assoc['isOwningSide']) { + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + + continue 2; + } + + $associatedId = array(); + + // TODO: Is this even computed right in all cases of composite keys? + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null; + + if ($joinColumnValue !== null) { + if ($targetClass->containsForeignIdentifier) { + $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue; + } else { + $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; + } + } + } + + if ( ! $associatedId) { + // Foreign key is NULL + $class->reflFields[$field]->setValue($entity, null); + $this->originalEntityData[$oid][$field] = null; + + continue; + } + + if ( ! isset($hints['fetchMode'][$class->name][$field])) { + $hints['fetchMode'][$class->name][$field] = $assoc['fetch']; + } + + // Foreign key is set + // Check identity map first + // FIXME: Can break easily with composite keys if join column values are in + // wrong order. The correct order is the one in ClassMetadata#identifier. + $relatedIdHash = implode(' ', $associatedId); + + switch (true) { + case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])): + $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash]; + + // If this is an uninitialized proxy, we are deferring eager loads, + // this association is marked as eager fetch, and its an uninitialized proxy (wtf!) + // then we can append this entity for eager loading! + if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && + isset($hints[self::HINT_DEFEREAGERLOAD]) && + !$targetClass->isIdentifierComposite && + $newValue instanceof Proxy && + $newValue->__isInitialized__ === false) { + + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + } + + break; + + case ($targetClass->subClasses): + // If it might be a subtype, it can not be lazy. There isn't even + // a way to solve this with deferred eager loading, which means putting + // an entity with subclasses at a *-to-one location is really bad! (performance-wise) + $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId); + break; + + default: + switch (true) { + // We are negating the condition here. Other cases will assume it is valid! + case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER): + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + // Deferred eager load only works for single identifier classes + case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite): + // TODO: Is there a faster approach? + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + default: + // TODO: This is very imperformant, ignore it? + $newValue = $this->em->find($assoc['targetEntity'], $associatedId); + break; + } + + // PERF: Inlined & optimized code from UnitOfWork#registerManaged() + $newValueOid = spl_object_hash($newValue); + $this->entityIdentifiers[$newValueOid] = $associatedId; + $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue; + + if ( + $newValue instanceof NotifyPropertyChanged && + ( ! $newValue instanceof Proxy || $newValue->__isInitialized()) + ) { + $newValue->addPropertyChangedListener($this); + } + $this->entityStates[$newValueOid] = self::STATE_MANAGED; + // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also! + break; + } + + $this->originalEntityData[$oid][$field] = $newValue; + $class->reflFields[$field]->setValue($entity, $newValue); + + if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) { + $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity); + } + + break; + + default: + // Inject collection + $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection); + $pColl->setOwner($entity, $assoc); + $pColl->setInitialized(false); + + $reflField = $class->reflFields[$field]; + $reflField->setValue($entity, $pColl); + + if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) { + $this->loadCollection($pColl); + $pColl->takeSnapshot(); + } + + $this->originalEntityData[$oid][$field] = $pColl; + break; + } + } + + if ($overrideLocalValues) { + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postLoad, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + + return $entity; + } + + /** + * @return void + */ + public function triggerEagerLoads() + { + if ( ! $this->eagerLoadingEntities) { + return; + } + + // avoid infinite recursion + $eagerLoadingEntities = $this->eagerLoadingEntities; + $this->eagerLoadingEntities = array(); + + foreach ($eagerLoadingEntities as $entityName => $ids) { + if ( ! $ids) { + continue; + } + + $class = $this->em->getClassMetadata($entityName); + + $this->getEntityPersister($entityName)->loadAll( + array_combine($class->identifier, array(array_values($ids))) + ); + } + } + + /** + * Initializes (loads) an uninitialized persistent collection of an entity. + * + * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize. + * + * @return void + * + * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. + */ + public function loadCollection(PersistentCollection $collection) + { + $assoc = $collection->getMapping(); + $persister = $this->getEntityPersister($assoc['targetEntity']); + + switch ($assoc['type']) { + case ClassMetadata::ONE_TO_MANY: + $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection); + break; + } + } + + /** + * Gets the identity map of the UnitOfWork. + * + * @return array + */ + public function getIdentityMap() + { + return $this->identityMap; + } + + /** + * Gets the original data of an entity. The original data is the data that was + * present at the time the entity was reconstituted from the database. + * + * @param object $entity + * + * @return array + */ + public function getOriginalEntityData($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->originalEntityData[$oid])) { + return $this->originalEntityData[$oid]; + } + + return array(); + } + + /** + * @ignore + * + * @param object $entity + * @param array $data + * + * @return void + */ + public function setOriginalEntityData($entity, array $data) + { + $this->originalEntityData[spl_object_hash($entity)] = $data; + } + + /** + * INTERNAL: + * Sets a property value of the original data array of an entity. + * + * @ignore + * + * @param string $oid + * @param string $property + * @param mixed $value + * + * @return void + */ + public function setOriginalEntityProperty($oid, $property, $value) + { + $this->originalEntityData[$oid][$property] = $value; + } + + /** + * Gets the identifier of an entity. + * The returned value is always an array of identifier values. If the entity + * has a composite identifier then the identifier values are in the same + * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). + * + * @param object $entity + * + * @return array The identifier values. + */ + public function getEntityIdentifier($entity) + { + return $this->entityIdentifiers[spl_object_hash($entity)]; + } + + /** + * Processes an entity instance to extract their identifier values. + * + * @param object $entity The entity instance. + * + * @return mixed A scalar value. + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function getSingleIdentifierValue($entity) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($class->isIdentifierComposite) { + throw ORMInvalidArgumentException::invalidCompositeIdentifier(); + } + + $values = $this->isInIdentityMap($entity) + ? $this->getEntityIdentifier($entity) + : $class->getIdentifierValues($entity); + + return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null; + } + + /** + * Tries to find an entity with the given identifier in the identity map of + * this UnitOfWork. + * + * @param mixed $id The entity identifier to look for. + * @param string $rootClassName The name of the root class of the mapped entity hierarchy. + * + * @return object|bool Returns the entity with the specified identifier if it exists in + * this UnitOfWork, FALSE otherwise. + */ + public function tryGetById($id, $rootClassName) + { + $idHash = implode(' ', (array) $id); + + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + return false; + } + + /** + * Schedules an entity for dirty-checking at commit-time. + * + * @param object $entity The entity to schedule for dirty-checking. + * + * @return void + * + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + $this->scheduledForDirtyCheck[$rootClassName][spl_object_hash($entity)] = $entity; + } + + /** + * Checks whether the UnitOfWork has any pending insertions. + * + * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise. + */ + public function hasPendingInsertions() + { + return ! empty($this->entityInsertions); + } + + /** + * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the + * number of entities in the identity map. + * + * @return integer + */ + public function size() + { + $countArray = array_map(function ($item) { return count($item); }, $this->identityMap); + + return array_sum($countArray); + } + + /** + * Gets the EntityPersister for an Entity. + * + * @param string $entityName The name of the Entity. + * + * @return \Doctrine\ORM\Persisters\BasicEntityPersister + */ + public function getEntityPersister($entityName) + { + if (isset($this->persisters[$entityName])) { + return $this->persisters[$entityName]; + } + + $class = $this->em->getClassMetadata($entityName); + + switch (true) { + case ($class->isInheritanceTypeNone()): + $persister = new Persisters\BasicEntityPersister($this->em, $class); + break; + + case ($class->isInheritanceTypeSingleTable()): + $persister = new Persisters\SingleTablePersister($this->em, $class); + break; + + case ($class->isInheritanceTypeJoined()): + $persister = new Persisters\JoinedSubclassPersister($this->em, $class); + break; + + default: + $persister = new Persisters\UnionSubclassPersister($this->em, $class); + } + + $this->persisters[$entityName] = $persister; + + return $this->persisters[$entityName]; + } + + /** + * Gets a collection persister for a collection-valued association. + * + * @param array $association + * + * @return \Doctrine\ORM\Persisters\AbstractCollectionPersister + */ + public function getCollectionPersister(array $association) + { + $type = $association['type']; + + if (isset($this->collectionPersisters[$type])) { + return $this->collectionPersisters[$type]; + } + + switch ($type) { + case ClassMetadata::ONE_TO_MANY: + $persister = new Persisters\OneToManyPersister($this->em); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister = new Persisters\ManyToManyPersister($this->em); + break; + } + + $this->collectionPersisters[$type] = $persister; + + return $this->collectionPersisters[$type]; + } + + /** + * INTERNAL: + * Registers an entity as managed. + * + * @param object $entity The entity. + * @param array $id The identifier values. + * @param array $data The original entity data. + * + * @return void + */ + public function registerManaged($entity, array $id, array $data) + { + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + + $this->addToIdentityMap($entity); + + if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * INTERNAL: + * Clears the property changeset of the entity with the given OID. + * + * @param string $oid The entity's OID. + * + * @return void + */ + public function clearEntityChangeSet($oid) + { + $this->entityChangeSets[$oid] = array(); + } + + /* PropertyChangedListener implementation */ + + /** + * Notifies this UnitOfWork of a property change in an entity. + * + * @param object $entity The entity that owns the property. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property. + * @param mixed $newValue The new value of the property. + * + * @return void + */ + public function propertyChanged($entity, $propertyName, $oldValue, $newValue) + { + $oid = spl_object_hash($entity); + $class = $this->em->getClassMetadata(get_class($entity)); + + $isAssocField = isset($class->associationMappings[$propertyName]); + + if ( ! $isAssocField && ! isset($class->fieldMappings[$propertyName])) { + return; // ignore non-persistent fields + } + + // Update changeset and mark entity for synchronization + $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue); + + if ( ! isset($this->scheduledForDirtyCheck[$class->rootEntityName][$oid])) { + $this->scheduleForDirtyCheck($entity); + } + } + + /** + * Gets the currently scheduled entity insertions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityInsertions() + { + return $this->entityInsertions; + } + + /** + * Gets the currently scheduled entity updates in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityUpdates() + { + return $this->entityUpdates; + } + + /** + * Gets the currently scheduled entity deletions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityDeletions() + { + return $this->entityDeletions; + } + + /** + * Gets the currently scheduled complete collection deletions + * + * @return array + */ + public function getScheduledCollectionDeletions() + { + return $this->collectionDeletions; + } + + /** + * Gets the currently scheduled collection inserts, updates and deletes. + * + * @return array + */ + public function getScheduledCollectionUpdates() + { + return $this->collectionUpdates; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * @param object $obj + * + * @return void + */ + public function initializeObject($obj) + { + if ($obj instanceof Proxy) { + $obj->__load(); + + return; + } + + if ($obj instanceof PersistentCollection) { + $obj->initialize(); + } + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } + + /** + * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit(). + * + * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information + * on this object that might be necessary to perform a correct update. + * + * @param object $object + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function markReadOnly($object) + { + if ( ! is_object($object) || ! $this->isInIdentityMap($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + $this->readOnlyObjects[spl_object_hash($object)] = true; + } + + /** + * Is this entity read only? + * + * @param object $object + * + * @return bool + * + * @throws ORMInvalidArgumentException + */ + public function isReadOnly($object) + { + if ( ! is_object($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + return isset($this->readOnlyObjects[spl_object_hash($object)]); + } + + private function dispatchOnFlushEvent() + { + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em)); + } + } + + private function dispatchPostFlushEvent() + { + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em)); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php new file mode 100644 index 00000000..f8347d99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Class to store and retrieve the version of Doctrine + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.4.0-RC1'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int Returns -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/symfony/console/Symfony/Component/Console/.gitignore b/vendor/symfony/console/Symfony/Component/Console/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/console/Symfony/Component/Console/Application.php b/vendor/symfony/console/Symfony/Component/Console/Application.php new file mode 100644 index 00000000..34a1f912 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Application.php @@ -0,0 +1,1144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\ProgressHelper; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + * + * @api + */ +class Application +{ + private $commands; + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions; + private $autoExit; + private $definition; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + * + * @api + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->catchExceptions = true; + $this->autoExit = true; + $this->commands = array(); + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + * + * @api + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + try { + $statusCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + $statusCode = $e->getCode(); + + $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; + } + + if ($this->autoExit) { + if ($statusCode > 255) { + $statusCode = 255; + } + // @codeCoverageIgnoreStart + exit($statusCode); + // @codeCoverageIgnoreEnd + } + + return $statusCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $name = $this->getCommandName($input); + + if (true === $input->hasParameterOption(array('--ansi'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } + + if (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) { + $inputStream = $this->getHelperSet()->get('dialog')->getInputStream(); + if (!posix_isatty($inputStream)) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + if (!$name) { + $name = 'list'; + $input = new ArrayInput(array('command' => 'list')); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $statusCode = $command->run($input, $output); + $this->runningCommand = null; + + return is_numeric($statusCode) ? $statusCode : 0; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + * + * @api + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + * + * @api + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application + * + * @param InputDefinition $definition The input definition + * + * @api + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + $messages = array( + $this->getLongVersion(), + '', + 'Usage:', + ' [options] command [arguments]', + '', + 'Options:', + ); + + foreach ($this->getDefinition()->getOptions() as $option) { + $messages[] = sprintf(' %-29s %s %s', + '--'.$option->getName().'', + $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', + $option->getDescription() + ); + } + + return implode(PHP_EOL, $messages); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param Boolean $boolean Whether to catch exceptions or not during commands execution + * + * @api + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (Boolean) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param Boolean $boolean Whether to automatically exit after a command execution or not + * + * @api + */ + public function setAutoExit($boolean) + { + $this->autoExit = (Boolean) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + * + * @api + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + * + * @api + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + * + * @api + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + * + * @api + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + * + * @api + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + * + * @api + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws \InvalidArgumentException When command name given does not exist + * + * @api + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return Boolean true if the command exists, false otherwise + * + * @api + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + $namespaces[] = $this->extractNamespace($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractNamespace($alias); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws \InvalidArgumentException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = array(); + foreach ($this->getNamespaces() as $n) { + $allNamespaces[$n] = explode(':', $n); + } + + $found = array(); + foreach (explode(':', $namespace) as $i => $part) { + $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces))))); + + if (!isset($abbrevs[$part])) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if (1 <= $i) { + $part = implode(':', $found).':'.$part; + } + + if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($abbrevs[$part]) > 1) { + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part]))); + } + + $found[] = $abbrevs[$part][0]; + } + + return implode(':', $found); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws \InvalidArgumentException When command name is incorrect or ambiguous + * + * @api + */ + public function find($name) + { + // namespace + $namespace = ''; + $searchName = $name; + if (false !== $pos = strrpos($name, ':')) { + $namespace = $this->findNamespace(substr($name, 0, $pos)); + $searchName = $namespace.substr($name, $pos); + } + + // name + $commands = array(); + foreach ($this->commands as $command) { + $extractedNamespace = $this->extractNamespace($command->getName()); + if ($extractedNamespace === $namespace + || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace) + ) { + $commands[] = $command->getName(); + } + } + + $abbrevs = static::getAbbreviations(array_unique($commands)); + if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) { + return $this->get($abbrevs[$searchName][0]); + } + + if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) { + $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]); + + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); + } + + // aliases + $aliases = array(); + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + $extractedNamespace = $this->extractNamespace($alias); + if ($extractedNamespace === $namespace + || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace) + ) { + $aliases[] = $alias; + } + } + } + + $aliases = static::getAbbreviations(array_unique($aliases)); + if (!isset($aliases[$searchName])) { + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($aliases[$searchName]) > 1) { + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName]))); + } + + return $this->get($aliases[$searchName][0]); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + * + * @api + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name) - 1; $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + if (!isset($abbrevs[$abbrev])) { + $abbrevs[$abbrev] = array($name); + } else { + $abbrevs[$abbrev][] = $name; + } + } + } + + // Non-abbreviations always get entered, even if they aren't unique + foreach ($names as $name) { + $abbrevs[$name] = array($name); + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param boolean $raw Whether to return raw command list + * + * @return string A string representing the Application + */ + public function asText($namespace = null, $raw = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + $width += 2; + + if ($raw) { + $messages = array(); + foreach ($this->sortCommands($commands) as $space => $commands) { + foreach ($commands as $name => $command) { + $messages[] = sprintf("%-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + $messages = array($this->getHelp(), ''); + if ($namespace) { + $messages[] = sprintf("Available commands for the \"%s\" namespace:", $namespace); + } else { + $messages[] = 'Available commands:'; + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace && '_global' !== $space) { + $messages[] = ''.$space.''; + } + + foreach ($commands as $name => $command) { + $messages[] = sprintf(" %-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the Application + */ + public function asXml($namespace = null, $asDom = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('symfony')); + + $xml->appendChild($commandsXML = $dom->createElement('commands')); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } else { + $namespacesXML = $dom->createElement('namespaces'); + $xml->appendChild($namespacesXML); + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace) { + $namespaceArrayXML = $dom->createElement('namespace'); + $namespacesXML->appendChild($namespaceArrayXML); + $namespaceArrayXML->setAttribute('id', $space); + } + + foreach ($commands as $name => $command) { + if ($name !== $command->getName()) { + continue; + } + + if (!$namespace) { + $commandXML = $dom->createElement('command'); + $namespaceArrayXML->appendChild($commandXML); + $commandXML->appendChild($dom->createTextNode($name)); + } + + $node = $command->asXml(true)->getElementsByTagName('command')->item(0); + $node = $dom->importNode($node, true); + + $commandsXML->appendChild($node); + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + /** + * Renders a caught exception. + * + * @param Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $strlen = function ($string) { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + }; + + do { + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach (str_split($line, $width - 4) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } + } + + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', max(0, $len - $strlen($title)))); + + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } + + $messages[] = str_repeat(' ', $len); + + $output->writeln(""); + $output->writeln(""); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln(""); + $output->writeln(""); + + if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; $i++) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + } + + $output->writeln(""); + $output->writeln(""); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); + $output->writeln(""); + $output->writeln(""); + } + } + + /** + * Tries to figure out the terminal width in which this application runs + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'), + new InputOption('--verbose', '-v', InputOption::VALUE_NONE, 'Increase verbosity of messages.'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DialogHelper(), + new ProgressHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output + * + * @return string x or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Sorts commands in alphabetical order. + * + * @param array $commands An associative array of commands to sort + * + * @return array A sorted array of commands + */ + private function sortCommands($commands) + { + $namespacedCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->extractNamespace($name, 1); + if (!$key) { + $key = '_global'; + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as &$commands) { + ksort($commands); + } + + return $namespacedCommands; + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + private function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative commands of $name + * + * @param string $name The full name of the command + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar commands + */ + private function findAlternativeCommands($name, $abbrevs) + { + $callback = function($item) { + return $item->getName(); + }; + + return $this->findAlternatives($name, $this->commands, $abbrevs, $callback); + } + + /** + * Finds alternative namespace of $name + * + * @param string $name The full name of the namespace + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar namespace + */ + private function findAlternativeNamespace($name, $abbrevs) + { + return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs + * + * @param string $name The string + * @param array|Traversable $collection The collection + * @param array $abbrevs The abbreviations + * @param Closure|string|array $callback The callable to transform collection item before comparison + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection, $abbrevs, $callback = null) + { + $alternatives = array(); + + foreach ($collection as $item) { + if (null !== $callback) { + $item = call_user_func($callback, $item); + } + + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + + if (!$alternatives) { + foreach ($abbrevs as $key => $values) { + $lev = levenshtein($name, $key); + if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + foreach ($values as $value) { + $alternatives[$value] = $lev; + } + } + } + } + + asort($alternatives); + + return array_keys($alternatives); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/CHANGELOG.md b/vendor/symfony/console/Symfony/Component/Console/CHANGELOG.md new file mode 100644 index 00000000..739b0e08 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/CHANGELOG.md @@ -0,0 +1,27 @@ +CHANGELOG +========= + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/console/Symfony/Component/Console/Command/Command.php b/vendor/symfony/console/Symfony/Component/Console/Command/Command.php new file mode 100644 index 00000000..f475ad53 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Command/Command.php @@ -0,0 +1,638 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + * + * @api + */ +class Command +{ + private $application; + private $name; + private $aliases; + private $definition; + private $help; + private $description; + private $ignoreValidationErrors; + private $applicationDefinitionMerged; + private $code; + private $synopsis; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the command + * + * @throws \LogicException When the command name is empty + * + * @api + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + $this->ignoreValidationErrors = false; + $this->applicationDefinitionMerged = false; + $this->aliases = array(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException('The command name cannot be empty.'); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + * + * @api + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + * + * @api + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return Boolean + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|integer null or 0 if everything went fine, or an error code + * + * @throws \LogicException When this abstract method is not implemented + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new \LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return integer The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + * + * @api + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws \InvalidArgumentException + * + * @see execute() + * + * @api + */ + public function setCode($code) + { + if (!is_callable($code)) { + throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.'); + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * @param Boolean $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + private function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || true === $this->applicationDefinitionMerged) { + return; + } + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + * + * @api + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + * + * @api + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + * + * @api + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE) + * + * @return Command The current instance + * + * @api + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws \InvalidArgumentException When command name given is empty + * + * @api + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + * + * @api + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + * + * @api + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + * + * @api + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + * + * @api + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%' + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name + ); + + return str_replace($placeholders, $replacements, $this->getHelp()); + } + + /** + * Sets the aliases for the command. + * + * @param array $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @api + */ + public function setAliases($aliases) + { + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + * + * @api + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @return string The synopsis + */ + public function getSynopsis() + { + if (null === $this->synopsis) { + $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis())); + } + + return $this->synopsis; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws \InvalidArgumentException if the helper is not defined + * + * @api + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + */ + public function asText() + { + if ($this->application && !$this->applicationDefinitionMerged) { + $this->getSynopsis(); + $this->mergeApplicationDefinition(false); + } + + $messages = array( + 'Usage:', + ' '.$this->getSynopsis(), + '', + ); + + if ($this->getAliases()) { + $messages[] = 'Aliases: '.implode(', ', $this->getAliases()).''; + } + + $messages[] = $this->getNativeDefinition()->asText(); + + if ($help = $this->getProcessedHelp()) { + $messages[] = 'Help:'; + $messages[] = ' '.str_replace("\n", "\n ", $help)."\n"; + } + + return implode("\n", $messages); + } + + /** + * Returns an XML representation of the command. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the command + */ + public function asXml($asDom = false) + { + if ($this->application && !$this->applicationDefinitionMerged) { + $this->getSynopsis(); + $this->mergeApplicationDefinition(false); + } + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($commandXML = $dom->createElement('command')); + $commandXML->setAttribute('id', $this->name); + $commandXML->setAttribute('name', $this->name); + + $commandXML->appendChild($usageXML = $dom->createElement('usage')); + $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), ''))); + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getProcessedHelp()))); + + $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); + foreach ($this->getAliases() as $alias) { + $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); + $aliasXML->appendChild($dom->createTextNode($alias)); + } + + $definition = $this->getNativeDefinition()->asXml(true); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true)); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true)); + + return $asDom ? $dom : $dom->saveXml(); + } + + private function validateName($name) + { + if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) { + throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.php b/vendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.php new file mode 100644 index 00000000..ac4dd3d5 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.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\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<%command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help as XML by using the --xml option: + + php %command.full_name% --xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + /** + * Sets the command + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + $output->writeln($this->command->asXml(), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->command->asText()); + } + + $this->command = null; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Command/ListCommand.php b/vendor/symfony/console/Symfony/Component/Console/Command/ListCommand.php new file mode 100644 index 00000000..ec5ea43e --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Command/ListCommand.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\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<%command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information as XML by using the --xml option: + + php %command.full_name% --xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption('xml')) { + $output->writeln($this->getApplication()->asXml($input->getArgument('namespace')), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->getApplication()->asText($input->getArgument('namespace'), $input->getOption('raw'))); + } + } + + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + )); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php new file mode 100644 index 00000000..642590dc --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.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\Console\Formatter; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatter implements OutputFormatterInterface +{ + /** + * The pattern to phrase the format. + */ + const FORMAT_PATTERN = '#(\\\\?)<(/?)([a-z][a-z0-9_=;-]+)?>((?: [^<\\\\]+ | (?!<(?:/?[a-z]|/>)). | .(?<=\\\\<) )*)#isx'; + + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + return preg_replace('/([^\\\\]?) FormatterStyle" instances + * + * @api + */ + public function __construct($decorated = null, array $styles = array()) + { + $this->decorated = (Boolean) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->decorated = (Boolean) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style isn't defined + * + * @api + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new \InvalidArgumentException('Undefined style: '.$name); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + public function format($message) + { + $message = preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $message); + + return str_replace('\\<', '<', $message); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Replaces style of the output. + * + * @param array $match + * + * @return string The replaced style + */ + private function replaceStyle($match) + { + // we got "\<" escaped char + if ('\\' === $match[1]) { + return $this->applyCurrentStyle($match[0]); + } + + if ('' === $match[3]) { + if ('/' === $match[2]) { + // we got "" tag + $this->styleStack->pop(); + + return $this->applyCurrentStyle($match[4]); + } + + // we got "<>" tag + return '<>'.$this->applyCurrentStyle($match[4]); + } + + if (isset($this->styles[strtolower($match[3])])) { + $style = $this->styles[strtolower($match[3])]; + } else { + $style = $this->createStyleFromString($match[3]); + + if (false === $style) { + return $this->applyCurrentStyle($match[0]); + } + } + + if ('/' === $match[2]) { + $this->styleStack->pop($style); + } else { + $this->styleStack->push($style); + } + + return $this->applyCurrentStyle($match[4]); + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|Boolean false if string is not format string + */ + private function createStyleFromString($string) + { + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + $style->setOption($match[1]); + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php new file mode 100644 index 00000000..08360842 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.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\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @api + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + public function format($message); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php new file mode 100644 index 00000000..ec471699 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.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 Symfony\Component\Console\Formatter; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37 + ); + private static $availableBackgroundColors = array( + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47 + ); + private static $availableOptions = array( + 'bold' => 1, + 'underscore' => 4, + 'blink' => 5, + 'reverse' => 7, + 'conceal' => 8 + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string $foreground The style foreground color name + * @param string $background The style background color name + * @param array $options The style options + * + * @api + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string $color The color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string $color The color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + * @api + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (false === array_search(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $codes = array(); + + if (null !== $this->foreground) { + $codes[] = $this->foreground; + } + if (null !== $this->background) { + $codes[] = $this->background; + } + if (count($this->options)) { + $codes = array_merge($codes, $this->options); + } + + if (0 === count($codes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 00000000..e8642b3c --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.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\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + * + * @api + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + * + * @api + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @api + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 00000000..5915023c --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.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\Console\Formatter; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface $style + * + * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new \InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles)-1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php b/vendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php new file mode 100644 index 00000000..170abd88 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.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\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class DialogHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks the user to select a value. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param array $choices List of choices to pick from + * @param Boolean $default The default answer if the user enters nothing + * @param Boolean|integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $errorMessage Message which will be shown if invalid value from choice list would be picked + * + * @return integer|string The selected value (the key of the choices array) + * + * @throws \InvalidArgumentException + */ + public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid') + { + $width = max(array_map('strlen', array_keys($choices))); + + $messages = (array) $question; + foreach ($choices as $key => $value) { + $messages[] = sprintf(" [%-${width}s] %s", $key, $value); + } + + $output->writeln($messages); + + $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage) { + if (empty($choices[$picked])) { + throw new \InvalidArgumentException(sprintf($errorMessage, $picked)); + } + + return $picked; + }, $attempts, $default); + + return $result; + } + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return string The user answer + * + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null) + { + $output->write($question); + + $inputStream = $this->inputStream ?: STDIN; + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } else { + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while ($c = fread($inputStream, 1)) { + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + $i--; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if ('A' === $c[2] || 'B' === $c[2]) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + $i++; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write('' . substr($matches[$ofs], $i) . ''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + } + + return strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks a question to the user, the response is hidden + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question + * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The answer + * + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponse(OutputInterface $output, $question, $fallback = true) + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $exe = __DIR__ . '/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir() . '/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $output->write($question); + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $output->write($question); + + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($this->inputStream ?: STDIN, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new \RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $output->write($question); + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + if ($fallback) { + return $this->ask($output, $question); + } + + throw new \RuntimeException('Unable to hide the response'); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null) + { + $that = $this; + + $interviewer = function() use ($output, $question, $default, $autocomplete, $that) { + return $that->ask($output, $question, $default, $autocomplete); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Asks for a value, hide and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param Boolean $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * + * @return string The response + * + * @throws \Exception When any of the validators return an error + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + * + */ + public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true) + { + $that = $this; + + $interviewer = function() use ($output, $question, $fallback, $that) { + return $that->askHiddenResponse($output, $question, $fallback); + }; + + return $this->validateAttempts($interviewer, $output, $validator, $attempts); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream + * + * @return string + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'dialog'; + } + + /** + * Return a valid unix shell + * + * @return string|Boolean The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } + + /** + * Validate an attempt + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param callable $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up ; false will ask infinitely + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts) + { + $error = null; + while (false === $attempts || $attempts--) { + if (null !== $error) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + try { + return call_user_func($validator, $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php b/vendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php new file mode 100644 index 00000000..20c754cc --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.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\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param Boolean $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + $messages = (array) $messages; + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + foreach ($messages as &$message) { + $message = sprintf('<%s>%s', $style, $message, $style); + } + + return implode("\n", $messages); + } + + /** + * Returns the length of a string, using mb_strlen if it is available. + * + * @param string $string The string to check its length + * + * @return integer The length of the string + */ + private function strlen($string) + { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/Helper.php b/vendor/symfony/console/Symfony/Component/Console/Helper/Helper.php new file mode 100644 index 00000000..28488caf --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/Helper.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\Console\Helper; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php b/vendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php new file mode 100644 index 00000000..6d394496 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.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\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + * + * @api + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + * + * @api + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + * + * @api + */ + public function getName(); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.php b/vendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.php new file mode 100644 index 00000000..d95c9a30 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.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\Console\Helper; + +use Symfony\Component\Console\Command\Command; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet +{ + private $helpers; + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + $this->helpers = array(); + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return Boolean true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws \InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php b/vendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php new file mode 100644 index 00000000..25736af6 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php @@ -0,0 +1,411 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * The Progress class providers helpers to display progress output. + * + * @author Chris Jones + * @author Fabien Potencier + */ +class ProgressHelper extends Helper +{ + const FORMAT_QUIET = ' %percent%%'; + const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%'; + const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%'; + const FORMAT_QUIET_NOMAX = ' %current%'; + const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]'; + const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%'; + + // options + private $barWidth = 28; + private $barChar = '='; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format = null; + private $redrawFreq = 1; + + private $lastMessagesLength; + private $barCharOriginal; + + /** + * @var OutputInterface + */ + private $output; + + /** + * Current step + * + * @var integer + */ + private $current; + + /** + * Maximum number of steps + * + * @var integer + */ + private $max; + + /** + * Start time of the progress bar + * + * @var integer + */ + private $startTime; + + /** + * List of formatting variables + * + * @var array + */ + private $defaultFormatVars = array( + 'current', + 'max', + 'bar', + 'percent', + 'elapsed', + ); + + /** + * Available formatting variables + * + * @var array + */ + private $formatVars; + + /** + * Stored format part widths (used for padding) + * + * @var array + */ + private $widths = array( + 'current' => 4, + 'max' => 4, + 'percent' => 3, + 'elapsed' => 6, + ); + + /** + * Various time formats + * + * @var array + */ + private $timeFormats = array( + array(0, '???'), + array(2, '1 sec'), + array(59, 'secs', 1), + array(60, '1 min'), + array(3600, 'mins', 60), + array(5400, '1 hr'), + array(86400, 'hrs', 3600), + array(129600, '1 day'), + array(604800, 'days', 86400), + ); + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int $freq The frequency in seconds + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = (int) $freq; + } + + /** + * Starts the progress output. + * + * @param OutputInterface $output An Output instance + * @param integer $max Maximum steps + */ + public function start(OutputInterface $output, $max = null) + { + $this->startTime = time(); + $this->current = 0; + $this->max = (int) $max; + $this->output = $output; + + if (null === $this->format) { + switch ($output->getVerbosity()) { + case OutputInterface::VERBOSITY_QUIET: + $this->format = self::FORMAT_QUIET_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_QUIET; + } + break; + case OutputInterface::VERBOSITY_VERBOSE: + $this->format = self::FORMAT_VERBOSE_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_VERBOSE; + } + break; + default: + $this->format = self::FORMAT_NORMAL_NOMAX; + if ($this->max > 0) { + $this->format = self::FORMAT_NORMAL; + } + break; + } + } + + $this->initialize(); + } + + /** + * Advances the progress output X steps. + * + * @param integer $step Number of steps to advance + * @param Boolean $redraw Whether to redraw or not + * + * @throws \LogicException + */ + public function advance($step = 1, $redraw = false) + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling advance().'); + } + + if ($this->current === 0) { + $redraw = true; + } + $this->current += $step; + if ($redraw || $this->current % $this->redrawFreq === 0) { + $this->display(); + } + } + + /** + * Outputs the current progress string. + * + * @param Boolean $finish Forces the end result + * + * @throws \LogicException + */ + public function display($finish = false) + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling display().'); + } + + $message = $this->format; + foreach ($this->generate($finish) as $name => $value) { + $message = str_replace("%{$name}%", $value, $message); + } + $this->overwrite($this->output, $message); + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (null === $this->startTime) { + throw new \LogicException('You must start the progress bar before calling finish().'); + } + + if ($this->startTime !== null) { + if (!$this->max) { + $this->barChar = $this->barCharOriginal; + $this->display(true); + } + $this->startTime = null; + $this->output->writeln(''); + $this->output = null; + } + } + + /** + * Initializes the progress helper. + */ + private function initialize() + { + $this->formatVars = array(); + foreach ($this->defaultFormatVars as $var) { + if (strpos($this->format, "%{$var}%") !== false) { + $this->formatVars[$var] = true; + } + } + + if ($this->max > 0) { + $this->widths['max'] = strlen($this->max); + $this->widths['current'] = $this->widths['max']; + } else { + $this->barCharOriginal = $this->barChar; + $this->barChar = $this->emptyBarChar; + } + } + + /** + * Generates the array map of format variables to values. + * + * @param Boolean $finish Forces the end result + * + * @return array Array of format vars and values + */ + private function generate($finish = false) + { + $vars = array(); + $percent = 0; + if ($this->max > 0) { + $percent = (double) round($this->current / $this->max, 2); + } + + if (isset($this->formatVars['bar'])) { + $completeBars = 0; + $emptyBars = 0; + if ($this->max > 0) { + $completeBars = floor($percent * $this->barWidth); + } else { + if (!$finish) { + $completeBars = floor($this->current % $this->barWidth); + } else { + $completeBars = $this->barWidth; + } + } + + $emptyBars = $this->barWidth - $completeBars - strlen($this->progressChar); + $bar = str_repeat($this->barChar, $completeBars); + if ($completeBars < $this->barWidth) { + $bar .= $this->progressChar; + $bar .= str_repeat($this->emptyBarChar, $emptyBars); + } + + $vars['bar'] = $bar; + } + + if (isset($this->formatVars['elapsed'])) { + $elapsed = time() - $this->startTime; + $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['current'])) { + $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT); + } + + if (isset($this->formatVars['max'])) { + $vars['max'] = $this->max; + } + + if (isset($this->formatVars['percent'])) { + $vars['percent'] = str_pad($percent * 100, $this->widths['percent'], ' ', STR_PAD_LEFT); + } + + return $vars; + } + + /** + * Converts seconds into human-readable format. + * + * @param integer $secs Number of seconds + * + * @return string Time in readable format + */ + private function humaneTime($secs) + { + $text = ''; + foreach ($this->timeFormats as $format) { + if ($secs < $format[0]) { + if (count($format) == 2) { + $text = $format[1]; + break; + } else { + $text = ceil($secs / $format[2]) . ' ' . $format[1]; + break; + } + } + } + + return $text; + } + + /** + * Overwrites a previous message to the output. + * + * @param OutputInterface $output An Output instance + * @param string|array $messages The message as an array of lines or a single string + */ + private function overwrite(OutputInterface $output, $messages) + { + // carriage return + $output->write("\x0D"); + if ($this->lastMessagesLength!==null) { + // clear the line with the length of the last message + $output->write(str_repeat("\x20", $this->lastMessagesLength)); + // carriage return + $output->write("\x0D"); + } + $output->write($messages); + + $this->lastMessagesLength=strlen($messages); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'progress'; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php b/vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php new file mode 100644 index 00000000..9addf148 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.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\Console\Input; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + * + * @api + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0]) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws \RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), true); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws \RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new \RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $v) { + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + foreach ($values as $value) { + if (0 === strpos($token, $value)) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.php b/vendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.php new file mode 100644 index 00000000..9921fffc --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + * + * @api + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k) && in_array($v, $values)) { + return true; + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + * @throws \InvalidArgumentException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/Input.php b/vendor/symfony/console/Symfony/Component/Console/Input/Input.php new file mode 100644 index 00000000..57f2972e --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/Input.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + protected $definition; + protected $options; + protected $arguments; + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->arguments = array(); + $this->options = array(); + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws \RuntimeException When not enough arguments are given + */ + public function validate() + { + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } + } + + /** + * Checks if the input is interactive. + * + * @return Boolean Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (Boolean) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/InputArgument.php b/vendor/symfony/console/Symfony/Component/Console/Input/InputArgument.php new file mode 100644 index 00000000..ffe97727 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/InputArgument.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\Console\Input; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + * + * @api + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws \InvalidArgumentException When argument mode is not valid + * + * @api + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return Boolean true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return Boolean true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.php b/vendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.php new file mode 100644 index 00000000..69d2f1ef --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.php @@ -0,0 +1,535 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + * + * @api + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + * + * @api + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + * + * @api + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + * + * @api + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + * + * @api + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws \LogicException When incorrect argument is given + * + * @api + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new \LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new \LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|integer $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws \InvalidArgumentException When argument given doesn't exist + * + * @api + */ + public function getArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + if (!$this->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + * + * @api + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + * + * @api + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return integer The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return integer The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + * + * @api + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + * + * @api + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws \LogicException When option given already exist + * + * @api + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } elseif (isset($this->shortcuts[$option->getShortcut()]) && !$option->equals($this->options[$this->shortcuts[$option->getShortcut()]])) { + throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $option->getShortcut())); + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + $this->shortcuts[$option->getShortcut()] = $option->getName(); + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws \InvalidArgumentException When option given doesn't exist + * + * @api + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + * + * @api + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + * + * @api + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws \InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @return string The synopsis + */ + public function getSynopsis() + { + $elements = array(); + foreach ($this->getOptions() as $option) { + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + } + + foreach ($this->getArguments() as $argument) { + $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); + + if ($argument->isArray()) { + $elements[] = sprintf('... [%sN]', $argument->getName()); + } + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + */ + public function asText() + { + // find the largest option or argument name + $max = 0; + foreach ($this->getOptions() as $option) { + $nameLength = strlen($option->getName()) + 2; + if ($option->getShortcut()) { + $nameLength += strlen($option->getShortcut()) + 3; + } + + $max = max($max, $nameLength); + } + foreach ($this->getArguments() as $argument) { + $max = max($max, strlen($argument->getName())); + } + ++$max; + + $text = array(); + + if ($this->getArguments()) { + $text[] = 'Arguments:'; + foreach ($this->getArguments() as $argument) { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $description = str_replace("\n", "\n".str_repeat(' ', $max + 2), $argument->getDescription()); + + $text[] = sprintf(" %-${max}s %s%s", $argument->getName(), $description, $default); + } + + $text[] = ''; + } + + if ($this->getOptions()) { + $text[] = 'Options:'; + + foreach ($this->getOptions() as $option) { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $multiple = $option->isArray() ? ' (multiple values allowed)' : ''; + $description = str_replace("\n", "\n".str_repeat(' ', $max + 2), $option->getDescription()); + + $optionMax = $max - strlen($option->getName()) - 2; + $text[] = sprintf(" %s %-${optionMax}s%s%s%s", + '--'.$option->getName(), + $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', + $description, + $default, + $multiple + ); + } + + $text[] = ''; + } + + return implode("\n", $text); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the InputDefinition + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($this->getArguments() as $argument) { + $argumentsXML->appendChild($argumentXML = $dom->createElement('argument')); + $argumentXML->setAttribute('name', $argument->getName()); + $argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $argumentXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $argumentXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($this->getOptions() as $option) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('name', '--'.$option->getName()); + $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + $optionXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $optionXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $optionXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + private function formatDefaultValue($default) + { + if (version_compare(PHP_VERSION, '5.4', '<')) { + return str_replace('\/', '/', json_encode($default)); + } + + return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/InputInterface.php b/vendor/symfony/console/Symfony/Component/Console/Input/InputInterface.php new file mode 100644 index 00000000..c39429d2 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/InputInterface.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\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return Boolean + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/InputOption.php b/vendor/symfony/console/Symfony/Component/Console/Input/InputOption.php new file mode 100644 index 00000000..70f49189 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/InputOption.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\Console\Input; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + * + * @api + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) + * + * @throws \InvalidArgumentException If option mode is invalid or incompatible + * + * @api + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new \InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if ('-' === $shortcut[0]) { + $shortcut = substr($shortcut, 1); + } + + if (empty($shortcut)) { + throw new \InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return Boolean true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one + * + * @param InputOption $option option to compare + * @return Boolean + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php b/vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php new file mode 100644 index 00000000..ecdb3e3d --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Input/StringInput.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\Console\Input; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + * + * @api + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^ ]+?)(?: |(?setTokens($this->tokenize($input)); + + if (null !== $definition) { + $this->bind($definition); + } + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws \InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input); + + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + // @codeCoverageIgnoreStart + throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + // @codeCoverageIgnoreEnd + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/LICENSE b/vendor/symfony/console/Symfony/Component/Console/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 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/console/Symfony/Component/Console/Output/ConsoleOutput.php b/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php new file mode 100644 index 00000000..46926c35 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + * + * @api + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatterInterface $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + $outputStream = 'php://stdout'; + if (!$this->hasStdoutSupport()) { + $outputStream = 'php://output'; + } + + parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter); + + $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter); + } + + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * @return OutputInterface + */ + public function getErrorOutput() + { + return $this->stderr; + } + + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * IBM iSeries (OS400) exhibits character-encoding issues when writing to + * STDOUT and doesn't properly convert ASCII to EBCDIC, resulting in garbage + * output. + * + * @return boolean + */ + protected function hasStdoutSupport() + { + return ('OS400' != php_uname('s')); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php b/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php new file mode 100644 index 00000000..5006b800 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.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\Console\Output; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * @return OutputInterface + */ + public function getErrorOutput(); + + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Output/NullOutput.php b/vendor/symfony/console/Symfony/Component/Console/Output/NullOutput.php new file mode 100644 index 00000000..783d42b6 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/NullOutput.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\Console\Output; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * + * @api + */ +class NullOutput extends Output +{ + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + protected function doWrite($message, $newline) + { + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Output/Output.php b/vendor/symfony/console/Symfony/Component/Console/Output/Output.php new file mode 100644 index 00000000..2493ae66 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/Output.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are three levels of verbosity: + * + * * normal: no option passed (normal output - information) + * * verbose: -v (more output - debug) + * * quiet: -q (no output) + * + * @author Fabien Potencier + * + * @api + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatterInterface $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = null === $formatter ? new OutputFormatter() : $formatter; + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + public function getVerbosity() + { + return $this->verbosity; + } + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines or a single string + * @param integer $type The type of output + * + * @api + */ + public function writeln($messages, $type = 0) + { + $this->write($messages, true, $type); + } + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function write($messages, $newline = false, $type = 0) + { + if (self::VERBOSITY_QUIET === $this->verbosity) { + return; + } + + $messages = (array) $messages; + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.php b/vendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.php new file mode 100644 index 00000000..6ca9f371 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + * + * @api + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; + + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output (0: normal, 1: raw, 2: plain) + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function write($messages, $newline = false, $type = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output (0: normal, 1: raw, 2: plain) + * + * @api + */ + public function writeln($messages, $type = 0); + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + public function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + public function getFormatter(); +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.php b/vendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.php new file mode 100644 index 00000000..831b2ae8 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.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\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + * + * @api + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param mixed $stream A stream resource + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatterInterface $formatter Output formatter instance + * + * @throws \InvalidArgumentException When first argument is not a real stream + * + * @api + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + * + * @throws \RuntimeException When unable to write output (should never happen) + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // @codeCoverageIgnoreStart + // should never happen + throw new \RuntimeException('Unable to write output.'); + // @codeCoverageIgnoreEnd + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - windows without ansicon and ConEmu + * - non tty consoles + * + * @return Boolean true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + // @codeCoverageIgnoreStart + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + // @codeCoverageIgnoreEnd + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/README.md b/vendor/symfony/console/Symfony/Component/Console/README.md new file mode 100644 index 00000000..fbf05fc8 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/README.md @@ -0,0 +1,63 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + + use Symfony\Component\Console\Application; + + $console = new Application(); + $console->run(); + +The ``run()`` method parses the arguments and options passed on the command +line and executes the right command. + +Registering a new command can easily be done via the ``register()`` method, +which returns a ``Command`` instance: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Input\InputOption; + use Symfony\Component\Console\Output\OutputInterface; + + $console + ->register('ls') + ->setDefinition(array( + new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'), + )) + ->setDescription('Displays the files in the given directory') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $dir = $input->getArgument('dir'); + + $output->writeln(sprintf('Dir listing for %s', $dir)); + }) + ; + +You can also register new commands via classes. + +The component provides a lot of features like output coloring, input and +output abstractions (so that you can easily unit-test your commands), +validation, automatic help messages, ... + +Tests +----- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Console/ + $ composer.phar install --dev + $ phpunit + +Third Party +----------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. + +Resources +--------- + +[The Console Component](http://symfony.com/doc/current/components/console.html) + +[How to create a Console Command](http://symfony.com/doc/current/cookbook/console/console_command.html) diff --git a/vendor/symfony/console/Symfony/Component/Console/Resources/bin/hiddeninput.exe b/vendor/symfony/console/Symfony/Component/Console/Resources/bin/hiddeninput.exe new file mode 100644 index 0000000000000000000000000000000000000000..c8cf65e8d819e6e525121cf6b21f1c2429746038 GIT binary patch literal 9216 zcmeHNe{@sVeZR8hV88~S)=Hp|Mpn({rC^@)BwNOI{ERJXCYlx+k1K6PLHo z_e!z_fhOzeA3JTX&-Z@s{rFOgjEwBlqjr!)9f zjyHz`A+ni`!0Taby{Uj5Y>jQq(k5A+X})PLWAi|{IZbtc8n^^trM{GI=P_15U6d?l zJJ3PW8XjfHpR}6`k{&5@JcEeH_SqQoQbU62o2YS30W)p_t&Fjy*RXQCZt$gCf|ao| zx&3R}m6|-Lfi@pua=$26n(UlnWo$>K67*|+#(qL_An=?l0M02AhOSJDv3;~?1ORfw z76EdK#MpSHqACHLcnJLIYlCSiX4eS@Pr8rN)Xwz0dk7O*y^0_C(Yks2Kvg! z-d-fJ)F9@k?>)m(XqDKIe2OKfhCQde9fpO0ko24yn*4xzX7q+ze`Z*=aJgwV?D?73 zaJ8UkSk|NN>@-|mB*f`EIK7$ElgAB<7p&p`^Vuq$58#;?B^*Bz7&d$B#+AYUC z(^m|`7{lqx&b^5$;i`j|S!+u|lcaQplp_&Nb)!>r>vGh3wb!tW zLq6%bkSt8jO|(vWH>LiPV(Xkp%BiGhl1q!PXXNKVKE!>Y5cHc2%cJOJA{-&ZsSn`T z#8~TA#(HWH4m>uCd+kCMTFgMI*s*n3!iCOwEI`{vGcVhzDu!Lw%-Ea^JATtrF`q3`+#KvvYJ0vM~A}D#LOD zlw`4ncB0U*Jji=--Wz#>I&5?hy;MgYW2u91d8ob=7MWfY`u;7Xe-J{Qsb0=0p|SM2 zG|=~mERIj4?gi)Ew|{LIN#oAsh20k_khIYjJBBN6rrIJ=eQO=nE;rTnPSiaQS$1$# z+|JRh0!IbQIa*f1(TZ}QM;|WO0+jTy(e)ggN4>zqp2E>C>hGPLHjHBh--2%@{EZNE zbUk{<3MABX&20QwK{MxK8`1Vk>^%dO5i@VTfu>NG3$K4NC=hSPsj9UYy`rNO}sBnB9QdKdIk7G+2_amnWstdTYVg z7HgLJGC~XLZG`63GwH8PdO_+G(k6~?J8Wj5mQos#21kC4W#2)guQXI)!z^{@F)U)5 z*re+r(2dib3D4P~%Z6TL=$PIkpmm<_#isu%t=%DcIwNkJhMeJ|bpahHO%8h|y~Ccf zUg#xVk+dyu>Q1O7JZ~8KS>tqi0qK**X*y6yHM71`bT=kFZ=@E%oe2!Km1^2sa>v+onZ%x_>aOJF+N0{i~z|<(IzgT*{0PpQq}E zQpU35@bm;qI?t_znGI&5&4sZV>+%m}w$(4hSDvLk)l<{5XyMlnCl7C%AjM3XnWvVz z{NoFsX)JB)SoqABZxUa*Yq+^^(cbq4mL%^lO12c${z{pf+)|kTTI~nQywyYF6}6|8 zlsN9&{-vwTrTyu<5^90_AsIU-ID#ZG@6d%poU44<**%xVe?`uxf}_Mr$SLHLS|K_N zQnw>(Lr2U=%$-<2D~RSzbG)2W2u^KMDnFFE?GmmbQ)V)fty957F`4OvQ_25E68ITr z5?`suu`|v?r!y=gFOGj$%9IJ zuTP=&2GcnoZZ0qSe6YL-*-lg>Q#>?Ew`a=GDc4vI#<1sNdKn?n7iSj0Orl$-#FMFi zykr>X-Xvi>sVr;92+8*H!r|3L$#o~hXa0z>AmF=z z?|@FF;*S|S0yqsw0j>Z(3mX-HD!|{N-vYc9paC8Ld=|6?00!6(_%lERupO`&um*4k z0b~W>e*uhTe4;V;mq>(ox$9FB`wLt!*DKj~!aOh|fL&#Pg*b??tm%5~_6M#02wqeC zS~wO>TWGnSp^r<0&8f2V6W->w=C+p~daC5e5wNQM*(* z66^}b0(!q3)zq$mu&VnbR#nr3;h5DS*o7{y66=!#;Dy4$pd1ZH<6WEOi0oJ8SxRL* z*v-9@Z^2w%^S(w5dO{_9Duby%2RT~;ppxaE$l()x6&}>7Wcg=u_&>f`Vs8OJGTy{X z2HpG=ThJz<{%|4Qq-~ad0qcrc87n88DHpM(nypwXIkZn<{zIT$ul&BQ?{ApCAZtyr zs2YpNt@x(G*faTU*HCKnAk(G=Tl~>r1QK8LY~J8mFFGoN5iIkYSwlm4Lsj#g4dsE5 zU-4;*Kdh-zv!rT4N$O}Q&n)?v0-9Y)lRFz58^P-KtKonzrfQ1p@0V_10^0||cGRn9 zRG<-#_TEV2nn4{BOh{YVBR4e!V!D?0K%BAlQN!D%M#k1bHypiIHT)5tlj>p0Pp_;+ z!cqC-JIs@JRhB+#teGs$Cib_=(yjRo4OJg^YPg%58aJVsC(LQ?W6%pn!-#aMZwoPcopo^Rn6BE z3=c5&W5~pP(C(-2r;PnH-S0{F`runM0ERCf3rESX$+S(MKOXmKJL9zXF}9-lf^xUs z+bb)+P%L&gV@<4q{6w^xEJ>Y>TQFUeoz0o-yq)jUqww=?wjUO8Y{a5G;DJ0Jr!LL+ zWhgsLuzi&eDrGDn$2DJwpFfH-?SGWbr>qRb?v{P`_%)So)CQgzO^HQ%;y#tJ=knH4 z95jX;^bF#BiuTH^%-j}{9VrZD=R%Q%wselH^p>5 z7d>gWB-st&3Fj%Mt*|tR5iK3J=`xhs&G)I7E>`FO@o7L z@S$B!pYMuzz5DN@X!O4DPm5n@raPJn-Q#o*m*e^5lk$g?0esg%$;>g5QW-|;c=H2GM}bo2tW^D924wmOkrUbWxcQ# z#v6bP%Tdfe~jtCRzAL;-OahZ=#yvUixu2-9fD2j$*|YY`F?0wF-{a# ztr<&kZjZ+81}6ZESqtgW)8kP#s@VLTSUR{}6?U^R*x7RE3Rl&n=VnFFqg9Uqz1n@N9N|=9<4} zuJfy^+}|D9X&vm3MAdqmu0&UMd^=K>b1hLAm_E!$rZC2b;;T~Dl zI`Eo_yRY76uM})|6wk9->of(=9&4jLv5#p@OzS~Yl>@pG)^>6`R+KtL{<4ly4o9WiM!%p_pfROU354)e8PIeE z1_s?#;OX6waNvvb&UQRN(WLbR+}&b#jo&WY-LlwCX}Q*$jGuKYuOGoIoyR(>e}}ix z+t}Q^cEcC8Y{@h}>HmJ^gD!l@gzwHmiBKl26x_lZVZG2UY!`w;RJd122;US&geQdW z3Qq}R!gIo5;ka;0I4c-Jq5X6A6?VzK&c4y!ZXdAUYu{r}*!SBXw?Aor+J4-A(*COb zb^CwV-?3k`zi-cX*c`VzL`RLI(b4MgIrGN z%ojf`E*6)Gg1A9!7q^N##2zsss^V9~-Qt7d!{UDNZ^XY9pA^3@9ui*?e=7c5d`nD; z?}~R(p>y1Kw!>|X4ycYEAkcZa*n-R%y! zqi)Up756UpqwfE7=hfigw$k~G@25gaxF9UGTkV>C(7x1Rbx4jb#|}rxq0vQ!n-c#f J0sQ~1{4brj`U(I5 literal 0 HcmV?d00001 diff --git a/vendor/symfony/console/Symfony/Component/Console/Shell.php b/vendor/symfony/console/Symfony/Component/Console/Shell.php new file mode 100644 index 00000000..f0faa35e --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Shell.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 Symfony\Component\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $prompt; + private $processIsolation; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + $this->prompt = $application->getName().' > '; + $this->processIsolation = false; + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<Running with process isolation, you should consider this: + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell ({$this->application->getVersion()}). + +At the prompt, type help for some help, +or list to get a list of available commands. + +To exit the shell, type ^D. + +EOF; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * + * @return Boolean|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->prompt); + } else { + $this->output->write($this->prompt); + $line = fgets(STDIN, 1024); + $line = (!$line && strlen($line) == 0) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (Boolean) $processIsolation; + + if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.'); + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php b/vendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php new file mode 100644 index 00000000..3c863542 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.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\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.php b/vendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.php new file mode 100644 index 00000000..c8405740 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.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\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function execute(array $input, array $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.php new file mode 100644 index 00000000..23edd490 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.php @@ -0,0 +1,660 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its first argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo version bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->setHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertEquals('Symfony\\Component\\Console\\Command\\HelpCommand', get_class($commands['help']), '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertEquals(1, count($commands), '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + try { + $application->get('foofoo'); + $this->fail('->get() throws an \InvalidArgumentException if the command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws an \InvalidArgumentException if the command does not exist'); + $this->assertEquals('The command "foofoo" does not exist.', $e->getMessage(), '->get() throws an \InvalidArgumentException if the command does not exist'); + } + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertEquals('Symfony\Component\Console\Command\HelpCommand', get_class($command), '->get() returns the help command if --help is provided as the input'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + try { + $application->findNamespace('f'); + $this->fail('->findNamespace() throws an \InvalidArgumentException if the abbreviation is ambiguous'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->findNamespace() throws an \InvalidArgumentException if the abbreviation is ambiguous'); + $this->assertEquals('The namespace "f" is ambiguous (foo, foo1).', $e->getMessage(), '->findNamespace() throws an \InvalidArgumentException if the abbreviation is ambiguous'); + } + + try { + $application->findNamespace('bar'); + $this->fail('->findNamespace() throws an \InvalidArgumentException if no command is in the given namespace'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->findNamespace() throws an \InvalidArgumentException if no command is in the given namespace'); + $this->assertEquals('There are no commands defined in the "bar" namespace.', $e->getMessage(), '->findNamespace() throws an \InvalidArgumentException if no command is in the given namespace'); + } + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('FooCommand', get_class($application->find('foo:bar')), '->find() returns a command if its name exists'); + $this->assertEquals('Symfony\Component\Console\Command\HelpCommand', get_class($application->find('h')), '->find() returns a command if its name exists'); + $this->assertEquals('FooCommand', get_class($application->find('f:bar')), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertEquals('FooCommand', get_class($application->find('f:b')), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertEquals('FooCommand', get_class($application->find('a')), '->find() returns a command if the abbreviation exists for an alias'); + + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find('f'); + $this->fail('->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a namespace'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a namespace'); + $this->assertRegExp('/Command "f" is not defined./', $e->getMessage(), '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a namespace'); + } + + try { + $application->find('a'); + $this->fail('->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for an alias'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for an alias'); + $this->assertEquals('Command "a" is ambiguous (afoobar, afoobar1 and 1 more).', $e->getMessage(), '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for an alias'); + } + + try { + $application->find('foo:b'); + $this->fail('->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a command'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a command'); + $this->assertEquals('Command "foo:b" is ambiguous (foo:bar, foo:bar1).', $e->getMessage(), '->find() throws an \InvalidArgumentException if the abbreviation is ambiguous for a command'); + } + } + + public function testFindAlternativeExceptionMessage() + { + $application = new Application(); + $application->add(new \FooCommand()); + + // Command + singular + try { + $application->find('foo:baR'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + $this->assertRegExp('/Did you mean this/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + } + + // Namespace + singular + try { + $application->find('foO:bar'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + $this->assertRegExp('/Did you mean this/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with one alternative'); + } + + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an \InvalidArgumentException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, without alternatives'); + } + + try { + $application->find($commandName = 'foo'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertRegExp('/foo:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar"'); + $this->assertRegExp('/foo1:bar/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo1:bar"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternative : "foo:bar1"'); + } + + // Test if "foo1" command throw an "\InvalidArgumentException" and does not contain + // "foo:bar" as alternative because "foo1" is too far from "foo:bar" + try { + $application->find($commandName = 'foo1'); + $this->fail('->find() throws an \InvalidArgumentException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist'); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with alternatives'); + $this->assertFalse(strpos($e->getMessage(), 'foo:bar'), '->find() throws an \InvalidArgumentException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws an \InvalidArgumentException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist'); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws an \InvalidArgumentException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist'); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->setCatchExceptions() sets the catch exception flag'); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + public function testAsText() + { + $application = new Application(); + $application->add(new \FooCommand); + $this->ensureStaticCommandHelp($application); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext1.txt', $this->normalizeLineBreaks($application->asText()), '->asText() returns a text representation of the application'); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_astext2.txt', $this->normalizeLineBreaks($application->asText('foo')), '->asText() returns a text representation of the application'); + } + + public function testAsXml() + { + $application = new Application(); + $application->add(new \FooCommand); + $this->ensureStaticCommandHelp($application); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml1.txt', $application->asXml(), '->asXml() returns an XML representation of the application'); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/application_asxml2.txt', $application->asXml('foo'), '->asXml() returns an XML representation of the application'); + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertSame('Symfony\Component\Console\Input\ArgvInput', get_class($command->input), '->run() creates an ArgvInput by default if none is given'); + $this->assertSame('Symfony\Component\Console\Output\ConsoleOutput', get_class($command->output), '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $this->normalizeLineBreaks($tester->getDisplay()), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + $this->assertTrue($helperSet->has('dialog')); + $this->assertTrue($helperSet->has('progress')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Command/CommandTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/CommandTest.php new file mode 100644 index 00000000..166ce787 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -0,0 +1,285 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + try { + $command = new Command(); + $this->fail('__construct() throws a \LogicException if the name is null'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '__construct() throws a \LogicException if the name is null'); + $this->assertEquals('The command name cannot be empty.', $e->getMessage(), '__construct() throws a \LogicException if the name is null'); + } + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + + try { + $command->setName(''); + $this->fail('->setName() throws an \InvalidArgumentException if the name is empty'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setName() throws an \InvalidArgumentException if the name is empty'); + $this->assertEquals('Command name "" is invalid.', $e->getMessage(), '->setName() throws an \InvalidArgumentException if the name is empty'); + } + + try { + $command->setName('foo:'); + $this->fail('->setName() throws an \InvalidArgumentException if the name is empty'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setName() throws an \InvalidArgumentException if the name is empty'); + $this->assertEquals('Command name "foo:" is invalid.', $e->getMessage(), '->setName() throws an \InvalidArgumentException if the name is empty'); + } + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('foo'); + $this->assertEquals('namespace:name [--foo] [foo]', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + public function testGet() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->__get() returns the correct helper'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testRun() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + try { + $tester->execute(array('--bar' => true)); + $this->fail('->run() throws a \InvalidArgumentException when the input does not validate the current InputDefinition'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->run() throws a \InvalidArgumentException when the input does not validate the current InputDefinition'); + $this->assertEquals('The "--bar" option does not exist.', $e->getMessage(), '->run() throws a \InvalidArgumentException when the input does not validate the current InputDefinition'); + } + + $tester->execute(array(), array('interactive' => true)); + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + + $tester->execute(array(), array('interactive' => false)); + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + + $command = new Command('foo'); + try { + $command->run(new StringInput(''), new NullOutput()); + $this->fail('->run() throws a \LogicException if the execute() method has not been overridden and no code has been provided'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->run() throws a \LogicException if the execute() method has not been overridden and no code has been provided'); + $this->assertEquals('You must override the execute() method in the concrete command class.', $e->getMessage(), '->run() throws a \LogicException if the execute() method has not been overridden and no code has been provided'); + } + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Invalid callable provided to Command::setCode. + */ + public function testSetCodeWithNonCallable() + { + $command = new \TestCommand(); + $command->setCode(array($this, 'nonExistentMethod')); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } + + public function testAsText() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertStringEqualsFile(self::$fixturesPath.'/command_astext.txt', $command->asText(), '->asText() returns a text representation of the command'); + } + + public function testAsXml() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $tester = new CommandTester($command); + $tester->execute(array('command' => $command->getName())); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Command/HelpCommandTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/HelpCommandTest.php new file mode 100644 index 00000000..b62973d9 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/HelpCommandTest.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\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecute() + { + $command = new HelpCommand(); + + $application = new Application(); + $command->setApplication($application); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li')); + $this->assertRegExp('/list \[--xml\] \[--raw\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + + $command = new HelpCommand(); + + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array()); + $this->assertRegExp('/list \[--xml\] \[--raw\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--xml' => true)); + $this->assertRegExp('/getDisplay(), '->execute() returns an XML help text if --xml is passed'); + + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertRegExp('/list \[--xml\] \[--raw\] \[namespace\]/', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + + $commandTester->execute(array('command_name' => 'list', '--xml' => true)); + $this->assertRegExp('/getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Command/ListCommandTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/ListCommandTest.php new file mode 100644 index 00000000..fb0eaccb --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Command/ListCommandTest.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\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecute() + { + $application = new Application(); + + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $this->assertRegExp('/help Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + + $commandTester->execute(array('command' => $command->getName(), '--xml' => true)); + $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<assertEquals(str_replace("\n", PHP_EOL, $output), $commandTester->getDisplay(), 'boo'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 00000000..254162f3 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 00000000..8071dc8f --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 00000000..7349bc35 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,25 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + throw new \Exception("First exception"); + } catch (\Exception $e) { + throw new \Exception("Second exception", 0, $e); + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 00000000..1c546399 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/FooCommand.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/FooCommand.php new file mode 100644 index 00000000..355e0ad6 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/TestCommand.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/TestCommand.php new file mode 100644 index 00000000..dcd32739 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 00000000..2f692c07 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v Increase verbosity of messages. + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands +foo + foo:bar The foo:bar command \ No newline at end of file diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 00000000..1457bf79 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v Increase verbosity of messages. + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +Available commands for the "foo" namespace: + foo:bar The foo:bar command \ No newline at end of file diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt new file mode 100644 index 00000000..ed168f25 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml1.txt @@ -0,0 +1,128 @@ + + + + + help [--xml] [command_name] + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help as XML by using the <comment>--xml</comment> option: + + <info>php app/console help --xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + + The command name + + help + + + + + + + + + + + + + + + + list [--xml] [--raw] [namespace] + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information as XML by using the <comment>--xml</comment> option: + + <info>php app/console list --xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + + The namespace name + + + + + + + + + + foo:bar + The foo:bar command + + + afoobar + + + + + + + + + + + + + + + + help + list + + + foo:bar + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt new file mode 100644 index 00000000..ef92f85c --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_asxml2.txt @@ -0,0 +1,37 @@ + + + + + foo:bar + The foo:bar command + + + afoobar + + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 00000000..5ad54200 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1,13 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v Increase verbosity of messages. + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. \ No newline at end of file diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 00000000..4629345c --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,8 @@ + + + + [InvalidArgumentException] + Command "foo" is not defined. + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 00000000..56dd52ec --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,11 @@ + + + + [InvalidArgumentException] + The "--foo" option does not exist. + + + +list [--xml] [--raw] [namespace] + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 00000000..c6399242 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,19 @@ + + + + [Exception] + Second exception + + + + + + + [Exception] + First exception + + + +foo3:bar + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 00000000..19f893b0 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,9 @@ + + + + [InvalidArgumentException] + Command "foo" is not define + d. + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run1.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run1.txt new file mode 100644 index 00000000..176dc881 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + [options] command [arguments] + +Options: + --help -h Display this help message. + --quiet -q Do not output any message. + --verbose -v Increase verbosity of messages. + --version -V Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction -n Do not ask any interactive question. + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run2.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run2.txt new file mode 100644 index 00000000..6831afdd --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,28 @@ +Usage: + help [--xml] [command_name] + +Arguments: + command The command to execute + command_name The command name (default: "help") + +Options: + --xml To output help as XML + --help (-h) Display this help message. + --quiet (-q) Do not output any message. + --verbose (-v) Increase verbosity of messages. + --version (-V) Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction (-n) Do not ask any interactive question. + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help as XML by using the --xml option: + + php app/console help --xml list + + To display the list of available commands, please use the list command. + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run3.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run3.txt new file mode 100644 index 00000000..441db548 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,27 @@ +Usage: + list [--xml] [--raw] [namespace] + +Arguments: + namespace The namespace name + +Options: + --xml To output help as XML + --raw To output raw command list + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information as XML by using the --xml option: + + php app/console list --xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run4.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run4.txt new file mode 100644 index 00000000..47187fc2 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_astext.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_astext.txt new file mode 100644 index 00000000..e5999030 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + +Aliases: name +Arguments: + command The command to execute + +Options: + --help (-h) Display this help message. + --quiet (-q) Do not output any message. + --verbose (-v) Increase verbosity of messages. + --version (-V) Display this application version. + --ansi Force ANSI output. + --no-ansi Disable ANSI output. + --no-interaction (-n) Do not ask any interactive question. + +Help: + help diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 00000000..806c5a56 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + namespace:name + description + help + + name + + + + The command to execute + + + + + + + + + + + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 00000000..a7d7e0d5 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +Arguments: + foo The foo argument + baz The baz argument (default: true) + bar The bar argument (default: ["http://foo.com/"]) + +Options: + --foo (-f) The foo option + --baz The baz option (default: false) + --bar (-b) The bar option (default: "bar") + --qux The qux option (default: ["http://foo.com/","bar"]) (multiple values allowed) + --qux2 The qux2 option (default: {"foo":"bar"}) (multiple values allowed) diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 00000000..eec8c079 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ + + + + + The foo argument + + + + The baz argument + + true + + + + The bar argument + + bar + + + + + + + + + diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 00000000..e99ebc57 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.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\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 00000000..b2875e9f --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.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\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[0m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[0m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[0m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[0m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[0m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[0m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[0m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[0m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[0m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[0m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[0m", $style->apply('foo')); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 00000000..497630e8 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.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\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class FormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals("foo<>bar", $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("fooformat('foo\\assertEquals("some info", $formatter->format('\\some info\\')); + $this->assertEquals("\\some info\\", OutputFormatter::escape('some info')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[0m", + $formatter->format('Symfony\Component\Console does work very well!') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[0m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[0m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[0m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[0m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[0m\033[32msome info\033[0m\033[37;41m error\033[0m", + $formatter->format('some some info error') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[0m)", + $formatter->format('(>=2.0,<2.3)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,format('('.$formatter->escape('z>=2.0,)') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[0m\033[32minfo\033[0m\033[33mcomment\033[0m\033[37;41merror\033[0m", + $formatter->format('errorinfocommenterror') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $this->assertEquals("\033[34;47msome custom msg\033[0m", $formatter->format('some custom msg')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[0m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[0m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals("\033[32msome \033[0m\033[32m styled\033[0m", $formatter->format('some styled')); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "some error", $formatter->format('some error') + ); + $this->assertEquals( + "some info", $formatter->format('some info') + ); + $this->assertEquals( + "some comment", $formatter->format('some comment') + ); + $this->assertEquals( + "some question", $formatter->format('some question') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[0m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[0m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[0m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[0m", $formatter->format('some question') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<format(<< +some text +EOF + )); + + $this->assertEquals(<<format(<<some text + +EOF + )); + + $this->assertEquals(<<format(<< +some text + +EOF + )); + + $this->assertEquals(<<format(<< +some text +more text + +EOF + )); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php new file mode 100644 index 00000000..12f27005 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/DialogHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; + +class DialogHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testSelect() + { + $dialog = new DialogHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $dialog->setInputStream($this->getInputStream("\n1\nFabien\n1\nFabien\nFabien\n")); + $this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2')); + $this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes)); + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!')); + + rewind($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream())); + + try { + $this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1)); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + } + + public function testAsk() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM')); + $this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM')); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\n"); + + $dialog = new DialogHelper(); + $dialog->setInputStream($inputStream); + + $bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles)); + } + + public function testAskHiddenResponse() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?')); + } + + public function testAskConfirmation() + { + $dialog = new DialogHelper(); + + $dialog->setInputStream($this->getInputStream("\n\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?')); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("y\nyes\n")); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + $this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false)); + + $dialog->setInputStream($this->getInputStream("n\nno\n")); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + $this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true)); + } + + public function testAskAndValidate() + { + $dialog = new DialogHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question ='What color was the white horse of Henry IV?'; + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fputs($stream, $input); + rewind($stream); + + return $stream; + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 00000000..5ab62a39 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '[cli] Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' Some text to display ', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' Some text to display ' . "\n" . + ' foo bar ', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' ' . "\n" . + ' Some text to display ' . "\n" . + ' ', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped('This test requires mbstring to work.'); + } + + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' ' . "\n" . + ' Du texte à afficher ' . "\n" . + ' ', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' ' . "\n" . + ' \some info\ ' . "\n" . + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.php new file mode 100644 index 00000000..b5560263 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Helper/ProgressHelperTest.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\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Output\StreamOutput; + +class ProgressHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAdvance() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1 [->--------------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceWithStep() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(5); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testAdvanceMultipleTimes() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream()); + $progress->advance(3); + $progress->advance(2); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 3 [--->------------------------]').$this->generateOutput(' 5 [----->----------------------]'), stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $progress = new ProgressHelper(); + $progress->setBarWidth(10); + $progress->setBarCharacter('_'); + $progress->setEmptyBarCharacter(' '); + $progress->setProgressCharacter('/'); + $progress->setFormat(' %current%/%max% [%bar%] %percent%%'); + $progress->start($output = $this->getOutputStream(), 10); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 1/10 [_/ ] 10%'), stream_get_contents($output->getStream())); + } + + public function testPercent() + { + $progress = new ProgressHelper(); + $progress->start($output = $this->getOutputStream(), 50); + $progress->display(); + $progress->advance(); + $progress->advance(); + + rewind($output->getStream()); + $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%').$this->generateOutput(' 1/50 [>---------------------------] 2%').$this->generateOutput(' 2/50 [=>--------------------------] 4%'), stream_get_contents($output->getStream())); + } + + protected function getOutputStream() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected $lastMessagesLength; + + protected function generateOutput($expected) + { + $expectedout = $expected; + + if ($this->lastMessagesLength !== null) { + $expectedout = str_repeat("\x20", $this->lastMessagesLength)."\x0D".$expected; + } + + $this->lastMessagesLength = strlen($expected); + + return "\x0D".$expectedout; + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArgvInputTest.php new file mode 100644 index 00000000..fc7ab218 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParser() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + + $input = new ArgvInput(array('cli.php', '--foo')); + $input->bind(new InputDefinition(array(new InputOption('foo')))); + $this->assertEquals(array('foo' => true), $input->getOptions(), '->parse() parses long options without a value'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses long options with a required value (with a = separator)'); + + $input = new ArgvInput(array('cli.php', '--foo', 'bar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses long options with a required value (with a space separator)'); + + try { + $input = new ArgvInput(array('cli.php', '--foo')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->fail('->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + $this->assertEquals('The "--foo" option requires a value.', $e->getMessage(), '->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + } + + $input = new ArgvInput(array('cli.php', '-f')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f')))); + $this->assertEquals(array('foo' => true), $input->getOptions(), '->parse() parses short options without a value'); + + $input = new ArgvInput(array('cli.php', '-fbar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses short options with a required value (with no separator)'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses short options with a required value (with a space separator)'); + + $input = new ArgvInput(array('cli.php', '-f', '')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => ''), $input->getOptions(), '->parse() parses short options with an optional empty value'); + + $input = new ArgvInput(array('cli.php', '-f', '', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => ''), $input->getOptions(), '->parse() parses short options with an optional empty value followed by an argument'); + + $input = new ArgvInput(array('cli.php', '-f', '', '-b')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')))); + $this->assertEquals(array('foo' => '', 'bar' => true), $input->getOptions(), '->parse() parses short options with an optional empty value followed by an option'); + + $input = new ArgvInput(array('cli.php', '-f', '-b', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')))); + $this->assertEquals(array('foo' => null, 'bar' => true), $input->getOptions(), '->parse() parses short options with an optional value which is not present'); + + try { + $input = new ArgvInput(array('cli.php', '-f')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->fail('->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + $this->assertEquals('The "--foo" option requires a value.', $e->getMessage(), '->parse() throws a \RuntimeException if no value is passed to an option when it is required'); + } + + try { + $input = new ArgvInput(array('cli.php', '-ffoo')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE)))); + $this->fail('->parse() throws a \RuntimeException if a value is passed to an option which does not take one'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if a value is passed to an option which does not take one'); + $this->assertEquals('The "-o" option does not exist.', $e->getMessage(), '->parse() throws a \RuntimeException if a value is passed to an option which does not take one'); + } + + try { + $input = new ArgvInput(array('cli.php', 'foo', 'bar')); + $input->bind(new InputDefinition()); + $this->fail('->parse() throws a \RuntimeException if too many arguments are passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if too many arguments are passed'); + $this->assertEquals('Too many arguments.', $e->getMessage(), '->parse() throws a \RuntimeException if too many arguments are passed'); + } + + try { + $input = new ArgvInput(array('cli.php', '--foo')); + $input->bind(new InputDefinition()); + $this->fail('->parse() throws a \RuntimeException if an unknown long option is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if an unknown long option is passed'); + $this->assertEquals('The "--foo" option does not exist.', $e->getMessage(), '->parse() throws a \RuntimeException if an unknown long option is passed'); + } + + try { + $input = new ArgvInput(array('cli.php', '-f')); + $input->bind(new InputDefinition()); + $this->fail('->parse() throws a \RuntimeException if an unknown short option is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() throws a \RuntimeException if an unknown short option is passed'); + $this->assertEquals('The "-f" option does not exist.', $e->getMessage(), '->parse() throws a \RuntimeException if an unknown short option is passed'); + } + + $input = new ArgvInput(array('cli.php', '-fb')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f'), new InputOption('bar', 'b')))); + $this->assertEquals(array('foo' => true, 'bar' => true), $input->getOptions(), '->parse() parses short options when they are aggregated as a single one'); + + $input = new ArgvInput(array('cli.php', '-fb', 'bar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)))); + $this->assertEquals(array('foo' => true, 'bar' => 'bar'), $input->getOptions(), '->parse() parses short options when they are aggregated as a single one and the last one has a required value'); + + $input = new ArgvInput(array('cli.php', '-fb', 'bar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => true, 'bar' => 'bar'), $input->getOptions(), '->parse() parses short options when they are aggregated as a single one and the last one has an optional value'); + + $input = new ArgvInput(array('cli.php', '-fbbar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => true, 'bar' => 'bar'), $input->getOptions(), '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator'); + + $input = new ArgvInput(array('cli.php', '-fbbar')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bbar', 'bar' => null), $input->getOptions(), '->parse() parses short options when they are aggregated as a single one and one of them takes a value'); + + try { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } catch (\RuntimeException $e) { + $this->assertNotEquals('Too many arguments.', $e->getMessage(), '->parse() parses array arguments'); + } + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions()); + + try { + $input = new ArgvInput(array('cli.php', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->fail('->parse() throws a \RuntimeException if an unknown option is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->parse() parses arguments with leading dashes as options without having encountered a double-dash sequence'); + $this->assertEquals('The "-1" option does not exist.', $e->getMessage(), '->parse() parses arguments with leading dashes as options without having encountered a double-dash sequence'); + } + + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertEquals('', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), 'dev'), + ); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArrayInputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArrayInputTest.php new file mode 100644 index 00000000..82f477ef --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/ArrayInputTest.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\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + } + + public function testParse() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + try { + $input = new ArrayInput(array('foo' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->fail('->parse() throws an \InvalidArgumentException exception if an invalid argument is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException exception if an invalid argument is passed'); + $this->assertEquals('The "foo" argument does not exist.', $e->getMessage(), '->parse() throws an \InvalidArgumentException exception if an invalid argument is passed'); + } + + $input = new ArrayInput(array('--foo' => 'bar'), new InputDefinition(array(new InputOption('foo')))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses long options'); + + $input = new ArrayInput(array('--foo' => 'bar'), new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses long options with a default value'); + + $input = new ArrayInput(array('--foo' => null), new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals(array('foo' => 'default'), $input->getOptions(), '->parse() parses long options with a default value'); + + try { + $input = new ArrayInput(array('--foo' => null), new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); + $this->fail('->parse() throws an \InvalidArgumentException exception if a required option is passed without a value'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException exception if a required option is passed without a value'); + $this->assertEquals('The "--foo" option requires a value.', $e->getMessage(), '->parse() throws an \InvalidArgumentException exception if a required option is passed without a value'); + } + + try { + $input = new ArrayInput(array('--foo' => 'foo'), new InputDefinition()); + $this->fail('->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + $this->assertEquals('The "--foo" option does not exist.', $e->getMessage(), '->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + } + + $input = new ArrayInput(array('-f' => 'bar'), new InputDefinition(array(new InputOption('foo', 'f')))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses short options'); + + try { + $input = new ArrayInput(array('-o' => 'foo'), new InputDefinition()); + $this->fail('->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + $this->assertEquals('The "-o" option does not exist.', $e->getMessage(), '->parse() throws an \InvalidArgumentException exception if an invalid option is passed'); + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputArgumentTest.php new file mode 100644 index 00000000..1d3144f6 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputArgumentTest.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\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + + // mode argument + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + + try { + $argument = new InputArgument('foo', 'ANOTHER_ONE'); + $this->fail('__construct() throws an \InvalidArgumentException if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the mode is not valid'); + $this->assertEquals('Argument mode "ANOTHER_ONE" is not valid.', $e->getMessage()); + } + try { + $argument = new InputArgument('foo', -1); + $this->fail('__construct() throws an \InvalidArgumentException if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the mode is not valid'); + $this->assertEquals('Argument mode "-1" is not valid.', $e->getMessage()); + } + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + + try { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + $this->fail('->setDefault() throws a \LogicException if you give a default value for a required argument'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->setDefault() throws a \LogicException exception if an invalid option is passed'); + $this->assertEquals('Cannot set a default value except for InputArgument::OPTIONAL mode.', $e->getMessage()); + } + + try { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + $this->fail('->setDefault() throws a \LogicException if you give a default value which is not an array for a IS_ARRAY option'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->setDefault() throws a \LogicException if you give a default value which is not an array for a IS_ARRAY option'); + $this->assertEquals('A default value for an array argument must be an array.', $e->getMessage()); + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 00000000..c322ed42 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputDefinitionTest.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\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo, $bar, $foo1, $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructor() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + + // arguments must have different names + try { + $definition->addArgument($this->foo1); + $this->fail('->addArgument() throws a \LogicException if another argument is already registered with the same name'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->addArgument() throws a \LogicException if another argument is already registered with the same name'); + $this->assertEquals('An argument with name "foo" already exists.', $e->getMessage()); + } + + // cannot add a parameter after an array parameter + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + try { + $definition->addArgument(new InputArgument('anotherbar')); + $this->fail('->addArgument() throws a \LogicException if there is an array parameter already registered'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->addArgument() throws a \LogicException if there is an array parameter already registered'); + $this->assertEquals('Cannot add an argument after an array argument.', $e->getMessage()); + } + + // cannot add a required argument after an optional one + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + try { + $definition->addArgument($this->foo2); + $this->fail('->addArgument() throws a \LogicException if you try to add a required argument after an optional one'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->addArgument() throws a \LogicException if you try to add a required argument after an optional one'); + $this->assertEquals('Cannot add a required argument after an optional one.', $e->getMessage()); + } + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + try { + $definition->getArgument('bar'); + $this->fail('->getArgument() throws an \InvalidArgumentException if the InputArgument name does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getArgument() throws an \InvalidArgumentException if the InputArgument name does not exist'); + $this->assertEquals('The "bar" argument does not exist.', $e->getMessage()); + } + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + try { + $definition->getOptionForShortcut('f'); + $this->fail('->setOptions() clears all InputOption objects'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOptions() clears all InputOption objects'); + $this->assertEquals('The "-f" option does not exist.', $e->getMessage()); + } + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + try { + $definition->addOption($this->foo2); + $this->fail('->addOption() throws a \LogicException if the another option is already registered with the same name'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->addOption() throws a \LogicException if the another option is already registered with the same name'); + $this->assertEquals('An option named "foo" already exists.', $e->getMessage()); + } + try { + $definition->addOption($this->foo1); + $this->fail('->addOption() throws a \LogicException if the another option is already registered with the same shortcut'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->addOption() throws a \LogicException if the another option is already registered with the same shortcut'); + $this->assertEquals('An option with shortcut "f" already exists.', $e->getMessage()); + } + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + try { + $definition->getOption('bar'); + $this->fail('->getOption() throws an \InvalidArgumentException if the option name does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getOption() throws an \InvalidArgumentException if the option name does not exist'); + $this->assertEquals('The "--bar" option does not exist.', $e->getMessage()); + } + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + try { + $definition->getOptionForShortcut('l'); + $this->fail('->getOption() throws an \InvalidArgumentException if the shortcut does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getOption() throws an \InvalidArgumentException if the shortcut does not exist'); + $this->assertEquals('The "-l" option does not exist.', $e->getMessage()); + } + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => null, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertEquals($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + public function testGetSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'))); + $this->assertEquals('[--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f'))); + $this->assertEquals('[-f|--foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))); + $this->assertEquals('[-f|--foo="..."]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))); + $this->assertEquals('[-f|--foo[="..."]]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + + $definition = new InputDefinition(array(new InputArgument('foo'))); + $this->assertEquals('[foo]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))); + $this->assertEquals('foo', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))); + $this->assertEquals('[foo1] ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + $definition = new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))); + $this->assertEquals('foo1 ... [fooN]', $definition->getSynopsis(), '->getSynopsis() returns a synopsis of arguments and options'); + } + + public function testAsText() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('http://foo.com/')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + new InputOption('qux', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux option', array('http://foo.com/', 'bar')), + new InputOption('qux2', '', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The qux2 option', array('foo' => 'bar')), + )); + $this->assertStringEqualsFile(self::$fixtures.'/definition_astext.txt', $definition->asText(), '->asText() returns a textual representation of the InputDefinition'); + } + + public function testAsXml() + { + $definition = new InputDefinition(array( + new InputArgument('foo', InputArgument::OPTIONAL, 'The foo argument'), + new InputArgument('baz', InputArgument::OPTIONAL, 'The baz argument', true), + new InputArgument('bar', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The bar argument', array('bar')), + new InputOption('foo', 'f', InputOption::VALUE_REQUIRED, 'The foo option'), + new InputOption('baz', null, InputOption::VALUE_OPTIONAL, 'The baz option', false), + new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL, 'The bar option', 'bar'), + )); + $this->assertXmlStringEqualsXmlFile(self::$fixtures.'/definition_asxml.txt', $definition->asXml(), '->asText() returns a textual representation of the InputDefinition'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputOptionTest.php new file mode 100644 index 00000000..1e0de35e --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputOptionTest.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\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + + try { + $option = new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + $this->fail('__construct() throws an \InvalidArgumentException if VALUE_IS_ARRAY option is used when an option does not accept a value'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if VALUE_IS_ARRAY option is used when an option does not accept a value'); + $this->assertEquals('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.', $e->getMessage()); + } + + // shortcut argument + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() removes the leading - of the shortcut'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + + // mode argument + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + + try { + $option = new InputOption('foo', 'f', 'ANOTHER_ONE'); + $this->fail('__construct() throws an \InvalidArgumentException if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the mode is not valid'); + $this->assertEquals('Option mode "ANOTHER_ONE" is not valid.', $e->getMessage()); + } + try { + $option = new InputOption('foo', 'f', -1); + $this->fail('__construct() throws an \InvalidArgumentException if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the mode is not valid'); + $this->assertEquals('Option mode "-1" is not valid.', $e->getMessage()); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + try { + $option->setDefault('default'); + $this->fail('->setDefault() throws a \LogicException if you give a default value for a VALUE_NONE option'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->setDefault() throws a \LogicException if you give a default value for a VALUE_NONE option'); + $this->assertEquals('Cannot set a default value when using InputOption::VALUE_NONE mode.', $e->getMessage()); + } + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + try { + $option->setDefault('default'); + $this->fail('->setDefault() throws a \LogicException if you give a default value which is not an array for a VALUE_IS_ARRAY option'); + } catch (\Exception $e) { + $this->assertInstanceOf('\LogicException', $e, '->setDefault() throws a \LogicException if you give a default value which is not an array for a VALUE_IS_ARRAY option'); + $this->assertEquals('A default value for an array option must be an array.', $e->getMessage()); + } + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputTest.php new file mode 100644 index 00000000..2863ea4f --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/InputTest.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\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + + try { + $input->setOption('foo', 'bar'); + $this->fail('->setOption() throws a \InvalidArgumentException if the option does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws a \InvalidArgumentException if the option does not exist'); + $this->assertEquals('The "foo" option does not exist.', $e->getMessage()); + } + + try { + $input->getOption('foo'); + $this->fail('->getOption() throws a \InvalidArgumentException if the option does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws a \InvalidArgumentException if the option does not exist'); + $this->assertEquals('The "foo" option does not exist.', $e->getMessage()); + } + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + + try { + $input->setArgument('foo', 'bar'); + $this->fail('->setArgument() throws a \InvalidArgumentException if the argument does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws a \InvalidArgumentException if the option does not exist'); + $this->assertEquals('The "foo" argument does not exist.', $e->getMessage()); + } + + try { + $input->getArgument('foo'); + $this->fail('->getArgument() throws a \InvalidArgumentException if the argument does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws a \InvalidArgumentException if the option does not exist'); + $this->assertEquals('The "foo" argument does not exist.', $e->getMessage()); + } + } + + public function testValidate() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + try { + $input->validate(); + $this->fail('->validate() throws a \RuntimeException if not enough arguments are given'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->validate() throws a \RuntimeException if not enough arguments are given'); + $this->assertEquals('Not enough arguments.', $e->getMessage()); + } + + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + try { + $input->validate(); + } catch (\RuntimeException $e) { + $this->fail('->validate() does not throw a \RuntimeException if enough arguments are given'); + } + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php new file mode 100644 index 00000000..1ce5fe62 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.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\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + + // definition in constructor + $input = new StringInput('--foo=bar', $definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array("--long-option=foo baranother"), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array("--long-option=foo baranother"), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 00000000..7a3ede3b --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.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\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Output/NullOutputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/NullOutputTest.php new file mode 100644 index 00000000..8dd5f7cd --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/NullOutputTest.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\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + $output->write('foo'); + $this->assertTrue(true, '->write() does nothing'); // FIXME + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Output/OutputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/OutputTest.php new file mode 100644 index 00000000..aa4a2046 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/OutputTest.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\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + } + + public function testWrite() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + + $output = new TestOutput(); + $output->writeln('foo', Output::OUTPUT_RAW); + $this->assertEquals("foo\n", $output->output, '->writeln() outputs the raw message if OUTPUT_RAW is specified'); + + $output = new TestOutput(); + $output->writeln('foo', Output::OUTPUT_PLAIN); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if OUTPUT_PLAIN is specified'); + + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('foo'); + $this->assertEquals("\033[33;41;5mfoo\033[0m\n", $output->output, '->writeln() decorates the output'); + + try { + $output->writeln('foo', 24); + $this->fail('->writeln() throws an \InvalidArgumentException when the type does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->writeln() throws an \InvalidArgumentException when the type does not exist'); + $this->assertEquals('Unknown output type given (24)', $e->getMessage()); + } + + $output->clear(); + $output->write('foo'); + $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Output/StreamOutputTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/StreamOutputTest.php new file mode 100644 index 00000000..b151a486 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Output/StreamOutputTest.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\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + try { + $output = new StreamOutput('foo'); + $this->fail('__construct() throws an \InvalidArgumentException if the first argument is not a stream'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the first argument is not a stream'); + $this->assertEquals('The StreamOutput class needs a stream as its first argument.', $e->getMessage()); + } + + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 00000000..6ce30ab0 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.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\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 00000000..da5bf84e --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/Tests/Tester/CommandTesterTest.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\Console\Tests\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/composer.json b/vendor/symfony/console/Symfony/Component/Console/composer.json new file mode 100644 index 00000000..33696815 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Console\\": "" } + }, + "target-dir": "Symfony/Component/Console", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + } +} diff --git a/vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist b/vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist new file mode 100644 index 00000000..8a7edd46 --- /dev/null +++ b/vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + +

+ Add new user +